创建string对象过程的内存分配
2012-04-20 17:50
357 查看
创建string对象过程的内存分配:
常量池(Constant Pool):指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。JVM虚拟机为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(String,Integer和 Floating point常量)和对其他类型,字段和方法的符号引用。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。
1、String s = "abc";
创建过程分析:在class文件被JVM装载到内存中,JVM会创建一块String Pool(String缓冲池)。当执行String s = “abc”;时,JVM首先在String Pool中查看是否存在字符串对象“abc”(如何查看呢?用equals()方法判断),如果已存在该对象,则不用创建新的字符串对象“abc”,而直接使用String Pool中已存在的对象“abc”,然后将引用s指向该对象;如果不存在该对象,则先在String Pool中创建一个新的字符串对象“abc”,然后将引用s指向String
Pool中创建的新对象。
注意:使用“字符串常量”引号创建的字符串对象时,在编译期就已经确定将该对象存储到String Pool中了。因此,String s = “abc”只会在编译期,在String Pool中创建一个对象。
例如:
Java代码
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
[java]
view plaincopyprint?
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
由于Sring类是不可变的,因而不必使用该构造方法,除非需要original的显式副本。
例如:
Java代码
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);//false
[java]
view plaincopyprint?
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);//false
运行结果:
true
true
true
结果说明:字符串常量生成的字符串对象在String Pool中只有一个拷贝,且它是在编译期就被确定了,所以“s1 == s2”;“abc”和“def”都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己也肯定是字符串常量(它在编译期就被解析为一个字符串对象了,即class文件中就已经存在“abcdef”),所以在字符串生成字符串对象时,s3也是String Pool中“abcdef”的一个引用。故JVM对于字符串常量的"+"号连接,在程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值。
例程2:
Java代码
String s1 = "abc";
String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
[java]
view plaincopyprint?
String s1 = "abc";
String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
运行结果如下:
true
true
false
false
例程4:
Java代码
final String s1 = "abc";
final String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
[java]
view plaincopyprint?
final String s1 = "abc";
final String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
Java代码
private static String getDef(){
return "def";
}
[java]
view plaincopyprint?
private static String getDef(){
return "def";
}
等价于:
Java代码
String s = "abc";
[java]
view plaincopyprint?
String s = "abc";
就不等价于:等价于:
Java代码
String s = "abc";
[java]
view plaincopyprint?
String s = "abc";
去看StringBuilder的toString()方法:
Java代码
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
[java]
view plaincopyprint?
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
Java代码
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
[java]
view plaincopyprint?
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
接下来我们再来看看intern()方法,它的定义如下:
Java代码
public native String intern();
[java]
view plaincopyprint?
public native String intern();
问题1:当执行完语句(1)时,在内存里面生成几个对象?它们是什么?在什么地方?
当执行完语句(1)时,在内存里面创建了两个对象,它们的内容分别都是abc,分别在String Pool(常量池)和Heap(堆)里。其字符串的创建过程如下:首先在String Pool里面查找查找是否有 "abc",如果有就直接使用,但这是本程序的第一条语句,故不存在一个对象"abc",所以要在String Pool中生成一个对象"abc",接下来,执行new String("abc")构造方法,new出来的对象都放在Heap里面。在Heap里又创建了一个"abc"的对象。这时内存里就有两个对象了,一个在String
Pool 里面,一个在Heap里面。
问题2:当执行完语句(2)时,在内存里面一共有几个对象?它们是什么?在什么地方?
当执行完语句(2)时,在内存里面一个对象也没有创建。当我们定义语句(2)的时候,如果我们用字符串的常量值(字面值)给s2赋值的话,那么首先JVM还是从String Pool里面去查找有没有内容为abc的这样一个对象存在,我们发现当我们执行完语句(1)的时候,StringPool里面已经存在了内容为abc的对象,那么就不会再在String Pool里面去生成内容为abc的字符串对象了。而是会使用已经存在String Pool里面的内容为abc的字符串对象,并且会将s2这个引用指向String Pool里面的内容为abc的字符串对象,s2存放的是String
Pool里面的内容为abc的字符串对像的地址。也就是说当你使用String s2 = "abc",即使用字符串常量("abc")给定义的引用(str2)赋值的话,那么它首先是在String Pool里面去找有没有内容为abc的字符串对象存在,如果有的话,就不用创建新的对象,直接引用String Pool里面已经存在的对象;如果没有的话,就在 String Pool里面去创建一个新的对象,接着将引用指向这个新创建的对象。所以,当执行完语句(2)时内存里面一共有2个对象,它们的内容分别都是abc,在String
Pool里面一个内容abc的对象,在Heap里面有一个内容为abc的对象。
问题3:当执行完语句(3)时,在内存里面一共有几个对象?它们是什么?在什么地方?
当执行完语句(3)时,其执行过程是这样的:它首先在String Pool里面去查找有没有内容为abc的字符串对象存在,发现有这个对象存在,它就不去创建 一个新的对象。接着执行new...,只要在java里面有关键字new存在,不管内容是否相同,都表示它将生成一个新的对象,new多少次,就生成多少个对象,而且新生成的对象都是在Heap里面,所以它会在Heap里面生成一个内容为abc的对象,并且将它的地址赋给了引用s3,s3就指向刚在Heap里面生成的内容为abc的对象。所以,当执行完语句(3)时,内存里面一共有3个对象,其中包含了在String
Pool里面一个内容为abc的字符串对象和在Heap里面包含了两个内容为abc的字符串对象。
问题4:当执行完语句(4)(5)(6)后,它们的结果分别是什么?
在java里面,对象用"=="永远比较的是两个对象的内存地址,换句话说,是比较"=="左右两边的两个引用是否指向同一个对象。对于java里面的8种原生数据类型来说,"=="比较的是它们的字面值是不是一样的;对应用类型来说,比较的是它们的内存地址是不是一样的。在语句(1)(2)(3)中,由于s1、s2、s3指向不同的对象,它们的内存地址就不一样,因此可以说当执行完语句(4)(5)(6),它们返回的结果都是false。
问题5:当执行完语句(7)(8)(9)后,它们的结果分别是什么?
首先,s1这个对象指向的是堆中第一次new...生成的对象,当调用 intern 方法时,如果String Pool已经包含一个等于此 String 对象的字符串(该对象由equals(Object)方法确定),则返回指向String Pool中的字符串对象的引用。因为String Pool中有内容为abc的对象,所以s1.intern()返回的是String Pool中的内容为abc的字符串对象的内存地址,而s1却是指向Heap上内容为abc的字符串对象的引用。因而,两个引用指向的对象不同,所以,s1 ==
s1.intern() 为false,即语句(7)结果为false。
对于s2.intern(),它还是会首先检查String Pool中是否有内容为abc的对象,发现有,则将String Pool中内容为abc的对象的地址赋给s2.intern()方法的返回值。因为s2和s2.intern()方法的返回值指向的是同一个对象,所以,s2 == s2.intern()的结果为true,,即语句(8)结果为true。
对于s1.intern(),它首先检查String Pool中是否有内容为abc的对象,发现有,则将String Pool中内容为abc的对象的赋给s1.intern()方法的返回值。对于s2.intern(),首先检查String Pool中是否有内容为abc的对象,发现有,则将String Pool中内容为abc的对象的地址赋给s2.intern()方法的返回值。因为两者返回的地址都指向同一个对象,所以,s1.intern() == s2.intern()的结果为true,,即是语句(9)结果为true。
因此,当执行完语句(7)(8)(9)后,它们的结果分别是false、true、true。
问题6:当执行完语句(13)(14) (15)(16)后,它们的结果分别是什么?
hello == "hello"引用hello指向的对象就是String Pool中的“hello”,即语句(13)的结果为true。
hello == "hel" + "lo"当加号两边都是常量值时,就会组成一个新的常量值"hello"在String Pool里面,如果String Pool已经有相同内容的就不会再创建,则直接返回String Pool里面的内容为"hello"的字符串对象的内存地址,所以,hello == "hel" + "lo"结果为true。
hello =="hel" + lo 当加号两边有一个不是常量值,会在堆里面创建一个新的"hello"对象,一个在String Pool中,一个在Heap中,故输出false 。
hel + lo 同上,输出false。
因此,当执行完语句(7)(8)(9)后,它们的结果分别是true、true、false、false。
例程7:
Java代码
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
s2.intern();//虽然执行了s2.intern(),但它的返回值没有赋给s2
s3 = s3.intern();//把String Pool中“abc”的引用赋给s3
System.out.println(s1 == s2);
System.out.println(s1 == s2.intern());
System.out.println(s1 == s3);
[java]
view plaincopyprint?
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
s2.intern();//虽然执行了s2.intern(),但它的返回值没有赋给s2
s3 = s3.intern();//把String Pool中“abc”的引用赋给s3
System.out.println(s1 == s2);
System.out.println(s1 == s2.intern());
System.out.println(s1 == s3);
运行结果如下:
false
false
false
结果说明:
s1指向String Pool中的字符串对象“abc”,编译期确定;
s2指向Heap中的字符串对象“abc”,运行期确定;
s3指向Heap中的另一个字符串对象“abc”,运行期确定。
注意:String s3 = "a" + new String("bc");等价于
String s3 = new StringBuilder().append("a").append(new String("bc")).toString();
思考:String s = "a" + new String("b") + "c";产生了那几个对象?
解:等价于String s = new StringBuilder().append("a").append(new String("b")).append("c").toString(),会在String Pool中产生"a"、"b"、"c"三个对象,在Heap中产生"b"、"abc"两个个对象。一共5个字符串对象。
例程9:
Java代码
String s1 = "Hello";
s1 = "Java";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s2 == s3);
[java]
view plaincopyprint?
String s1 = "Hello";
s1 = "Java";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s2 == s3);
String s1 = "Hello";
s1 = "Java";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s2 == s3);
运行结果如下:
false
false
分析这段程序的执行过程:
首先在加载Java程序时,JVM会创建一片的内存空间(String Pool)专门存入string对象。
String s1 = "Hello",现在栈中创建一个字符串引用s1,然后JVM会在String Pool中查找是否存在"Hello",如果存在,则直接使用它,将其地址赋给s1,如果不存在(这时String Pool中显然不存在"Hello"),则在String Pool中创建"Hello",并将其地址赋给s1。
s1 = "Java",JVM会在String Pool中查找是否存在"Java",如果存在,则直接使用它,将其地址赋给s1,如果不存在(这时String Pool中显然不存在"Java"),则在String Pool中创建"Java",并将其地址赋给s1。而原来的字符串对象"Hello"仍然在String Pool中,没有消失,因为String对象的值是不能被修改的。这里只是改变了引用的值即引用指向的对象的地址,而没有改变它所引用的对象。
String s2 = "Hello",JVM会在String Pool里查看有没有字符串"Hello",若有,则返回它的地址给s2,否则,创建新的String对象"Hello", 放到String Pool里。这里由于"Hello"对象已经创建,并存在于String Pool中,因而不需要重新创建String对象"Hello"。此时s1指向String Pool中的"Java",s2指向String Pool中的"Hello",故s1 == s2的值为false。
String s3=String("Hello"),JVM会在String Pool里查看有没有字符串"Hello",若有,直接执行new操作,若没有,则先要在String Pool中创建"Hello",然后执行new操作,由于遇到了new,还会在Heap上(不是String Pool里)创建string对象"Hello",并将Heap上的"Hello"对象的地址赋给引用s3。所以s2 == s3将返回false,因为s2和s3不是引用同一个对象。
以上小结如理解有误,希望各位高手指教!
常量池(Constant Pool):指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。JVM虚拟机为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(String,Integer和 Floating point常量)和对其他类型,字段和方法的符号引用。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。
1、String s = "abc";
创建过程分析:在class文件被JVM装载到内存中,JVM会创建一块String Pool(String缓冲池)。当执行String s = “abc”;时,JVM首先在String Pool中查看是否存在字符串对象“abc”(如何查看呢?用equals()方法判断),如果已存在该对象,则不用创建新的字符串对象“abc”,而直接使用String Pool中已存在的对象“abc”,然后将引用s指向该对象;如果不存在该对象,则先在String Pool中创建一个新的字符串对象“abc”,然后将引用s指向String
Pool中创建的新对象。
注意:使用“字符串常量”引号创建的字符串对象时,在编译期就已经确定将该对象存储到String Pool中了。因此,String s = “abc”只会在编译期,在String Pool中创建一个对象。
例如:
Java代码
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
[java]
view plaincopyprint?
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//true
Java代码 public String(String original) [java] view plaincopyprint? public String(String original) public String(String original)这个构造方法,作用:初始化一个新创建的String对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。
由于Sring类是不可变的,因而不必使用该构造方法,除非需要original的显式副本。
例如:
Java代码
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);//false
[java]
view plaincopyprint?
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);//false
Java代码 String s1 = "abcdef"; String s2 = "abcdef"; String s3 = "abc"+"def";//编译期自动优化为String s3 = "abcdef"; System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3); [java] view plaincopyprint? String s1 = "abcdef"; String s2 = "abcdef"; String s3 = "abc"+"def";//编译期自动优化为String s3 = "abcdef"; System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3); String s1 = "abcdef"; String s2 = "abcdef"; String s3 = "abc"+"def";//编译期自动优化为String s3 = "abcdef"; System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3);
运行结果:
true
true
true
结果说明:字符串常量生成的字符串对象在String Pool中只有一个拷贝,且它是在编译期就被确定了,所以“s1 == s2”;“abc”和“def”都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己也肯定是字符串常量(它在编译期就被解析为一个字符串对象了,即class文件中就已经存在“abcdef”),所以在字符串生成字符串对象时,s3也是String Pool中“abcdef”的一个引用。故JVM对于字符串常量的"+"号连接,在程序编译期,JVM就将常量字符串的"+"连接优化为连接后的值。
例程2:
Java代码
String s1 = "abc";
String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
[java]
view plaincopyprint?
String s1 = "abc";
String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
Java代码 final String s1 = "abc"; String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); [java] view plaincopyprint? final String s1 = "abc"; String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7); final String s1 = "abc"; String s2 = "def"; String s3 = "abcdef"; String s4 = "abc"+"def"; String s5 = s1 + "def"; String s6 = "abc"+s2; String s7 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); System.out.println(s3 == s6); System.out.println(s3 == s7);
运行结果如下:
true
true
false
false
例程4:
Java代码
final String s1 = "abc";
final String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
[java]
view plaincopyprint?
final String s1 = "abc";
final String s2 = "def";
String s3 = "abcdef";
String s4 = "abc"+"def";
String s5 = s1 + "def";
String s6 = "abc"+s2;
String s7 = s1 + s2;
System.out.println(s3 == s4);
System.out.println(s3 == s5);
System.out.println(s3 == s6);
System.out.println(s3 == s7);
Java代码 public static void main(String args[]){ String s1 = "abc"; final String s2 = getDef(); String s3 = "abcdef"; String s4 = "abc"+s2; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); } [java] view plaincopyprint? public static void main(String args[]){ String s1 = "abc"; final String s2 = getDef(); String s3 = "abcdef"; String s4 = "abc"+s2; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); } public static void main(String args[]){ String s1 = "abc"; final String s2 = getDef(); String s3 = "abcdef"; String s4 = "abc"+s2; String s5 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3 == s5); }
Java代码
private static String getDef(){
return "def";
}
[java]
view plaincopyprint?
private static String getDef(){
return "def";
}
Java代码 String s = "a" + "b" + "c"; [java] view plaincopyprint? String s = "a" + "b" + "c"; String s = "a" + "b" + "c";
等价于:
Java代码
String s = "abc";
[java]
view plaincopyprint?
String s = "abc";
Java代码 String a = "a"; String b = "b"; String c = "c"; String s = a + b + c; [java] view plaincopyprint? String a = "a"; String b = "b"; String c = "c"; String s = a + b + c; String a = "a"; String b = "b"; String c = "c"; String s = a + b + c;
就不等价于:等价于:
Java代码
String s = "abc";
[java]
view plaincopyprint?
String s = "abc";
Java代码 StringBuilder builder = new StringBuilder (); builder.append(a); builder.append(b); builder.append(c); String s = builder.toString(); [java] view plaincopyprint? StringBuilder builder = new StringBuilder (); builder.append(a); builder.append(b); builder.append(c); String s = builder.toString(); StringBuilder builder = new StringBuilder (); builder.append(a); builder.append(b); builder.append(c); String s = builder.toString();
去看StringBuilder的toString()方法:
Java代码
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
[java]
view plaincopyprint?
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
Java代码 String str1 = "abc";//是字符串常量,它在编译期被确定,放在常量池中(共享内容值) //new String()创建的字符串不放入常量池中 String str2 =new String("abc");//不是字符串常量,不在编译期确定(不共享内容值) [java] view plaincopyprint? String str1 = "abc";//是字符串常量,它在编译期被确定,放在常量池中(共享内容值) //new String()创建的字符串不放入常量池中 String str2 =new String("abc");//不是字符串常量,不在编译期确定(不共享内容值) String str1 = "abc";//是字符串常量,它在编译期被确定,放在常量池中(共享内容值) //new String()创建的字符串不放入常量池中 String str2 =new String("abc");//不是字符串常量,不在编译期确定(不共享内容值)
Java代码
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
[java]
view plaincopyprint?
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false
Java代码 String str1 = "abc"; String str2 = new String("abc"); System.out.println(str1==str2); //false [java] view plaincopyprint? String str1 = "abc"; String str2 = new String("abc"); System.out.println(str1==str2); //false String str1 = "abc"; String str2 = new String("abc"); System.out.println(str1==str2); //false
创建了两个引用。创建了两个对象。两个引用分别指向不同的两个对象。
接下来我们再来看看intern()方法,它的定义如下:
Java代码
public native String intern();
[java]
view plaincopyprint?
public native String intern();
Java代码 public class TestString{ public static void main(String args[]){ String s1 = new String("abc");//语句1 String s2 = "abc";//语句2 String s3 = new String("abc");//语句3 System.out.println(s1 == s2);//语句4 System.out.println(s1 == s3);//语句5 System.out.println(s2 == s3);//语句6 System.out.println(s1 == s1.intern());//语句7 System.out.println(s2 == s2.intern());//语句8 System.out.println(s1.intern() == s2.intern());//语句9 String hello = "hello";//语句10 String hel = "hel";//语句11 String lo = "lo";//语句12 System.out.println(hello == "hello");//语句13 System.out.println(hello == "hel" + "lo");//语句14 System.out.println(hello == "hel" + lo);//语句15 System.out.println(hello == hel + lo);//语句16 } } [java] view plaincopyprint? public class TestString{ public static void main(String args[]){ String s1 = new String("abc");//语句1 String s2 = "abc";//语句2 String s3 = new String("abc");//语句3 System.out.println(s1 == s2);//语句4 System.out.println(s1 == s3);//语句5 System.out.println(s2 == s3);//语句6 System.out.println(s1 == s1.intern());//语句7 System.out.println(s2 == s2.intern());//语句8 System.out.println(s1.intern() == s2.intern());//语句9 String hello = "hello";//语句10 String hel = "hel";//语句11 String lo = "lo";//语句12 System.out.println(hello == "hello");//语句13 System.out.println(hello == "hel" + "lo");//语句14 System.out.println(hello == "hel" + lo);//语句15 System.out.println(hello == hel + lo);//语句16 } } public class TestString{ public static void main(String args[]){ String s1 = new String("abc");//语句1 String s2 = "abc";//语句2 String s3 = new String("abc");//语句3 System.out.println(s1 == s2);//语句4 System.out.println(s1 == s3);//语句5 System.out.println(s2 == s3);//语句6 System.out.println(s1 == s1.intern());//语句7 System.out.println(s2 == s2.intern());//语句8 System.out.println(s1.intern() == s2.intern());//语句9 String hello = "hello";//语句10 String hel = "hel";//语句11 String lo = "lo";//语句12 System.out.println(hello == "hello");//语句13 System.out.println(hello == "hel" + "lo");//语句14 System.out.println(hello == "hel" + lo);//语句15 System.out.println(hello == hel + lo);//语句16 } }
问题1:当执行完语句(1)时,在内存里面生成几个对象?它们是什么?在什么地方?
当执行完语句(1)时,在内存里面创建了两个对象,它们的内容分别都是abc,分别在String Pool(常量池)和Heap(堆)里。其字符串的创建过程如下:首先在String Pool里面查找查找是否有 "abc",如果有就直接使用,但这是本程序的第一条语句,故不存在一个对象"abc",所以要在String Pool中生成一个对象"abc",接下来,执行new String("abc")构造方法,new出来的对象都放在Heap里面。在Heap里又创建了一个"abc"的对象。这时内存里就有两个对象了,一个在String
Pool 里面,一个在Heap里面。
问题2:当执行完语句(2)时,在内存里面一共有几个对象?它们是什么?在什么地方?
当执行完语句(2)时,在内存里面一个对象也没有创建。当我们定义语句(2)的时候,如果我们用字符串的常量值(字面值)给s2赋值的话,那么首先JVM还是从String Pool里面去查找有没有内容为abc的这样一个对象存在,我们发现当我们执行完语句(1)的时候,StringPool里面已经存在了内容为abc的对象,那么就不会再在String Pool里面去生成内容为abc的字符串对象了。而是会使用已经存在String Pool里面的内容为abc的字符串对象,并且会将s2这个引用指向String Pool里面的内容为abc的字符串对象,s2存放的是String
Pool里面的内容为abc的字符串对像的地址。也就是说当你使用String s2 = "abc",即使用字符串常量("abc")给定义的引用(str2)赋值的话,那么它首先是在String Pool里面去找有没有内容为abc的字符串对象存在,如果有的话,就不用创建新的对象,直接引用String Pool里面已经存在的对象;如果没有的话,就在 String Pool里面去创建一个新的对象,接着将引用指向这个新创建的对象。所以,当执行完语句(2)时内存里面一共有2个对象,它们的内容分别都是abc,在String
Pool里面一个内容abc的对象,在Heap里面有一个内容为abc的对象。
问题3:当执行完语句(3)时,在内存里面一共有几个对象?它们是什么?在什么地方?
当执行完语句(3)时,其执行过程是这样的:它首先在String Pool里面去查找有没有内容为abc的字符串对象存在,发现有这个对象存在,它就不去创建 一个新的对象。接着执行new...,只要在java里面有关键字new存在,不管内容是否相同,都表示它将生成一个新的对象,new多少次,就生成多少个对象,而且新生成的对象都是在Heap里面,所以它会在Heap里面生成一个内容为abc的对象,并且将它的地址赋给了引用s3,s3就指向刚在Heap里面生成的内容为abc的对象。所以,当执行完语句(3)时,内存里面一共有3个对象,其中包含了在String
Pool里面一个内容为abc的字符串对象和在Heap里面包含了两个内容为abc的字符串对象。
问题4:当执行完语句(4)(5)(6)后,它们的结果分别是什么?
在java里面,对象用"=="永远比较的是两个对象的内存地址,换句话说,是比较"=="左右两边的两个引用是否指向同一个对象。对于java里面的8种原生数据类型来说,"=="比较的是它们的字面值是不是一样的;对应用类型来说,比较的是它们的内存地址是不是一样的。在语句(1)(2)(3)中,由于s1、s2、s3指向不同的对象,它们的内存地址就不一样,因此可以说当执行完语句(4)(5)(6),它们返回的结果都是false。
问题5:当执行完语句(7)(8)(9)后,它们的结果分别是什么?
首先,s1这个对象指向的是堆中第一次new...生成的对象,当调用 intern 方法时,如果String Pool已经包含一个等于此 String 对象的字符串(该对象由equals(Object)方法确定),则返回指向String Pool中的字符串对象的引用。因为String Pool中有内容为abc的对象,所以s1.intern()返回的是String Pool中的内容为abc的字符串对象的内存地址,而s1却是指向Heap上内容为abc的字符串对象的引用。因而,两个引用指向的对象不同,所以,s1 ==
s1.intern() 为false,即语句(7)结果为false。
对于s2.intern(),它还是会首先检查String Pool中是否有内容为abc的对象,发现有,则将String Pool中内容为abc的对象的地址赋给s2.intern()方法的返回值。因为s2和s2.intern()方法的返回值指向的是同一个对象,所以,s2 == s2.intern()的结果为true,,即语句(8)结果为true。
对于s1.intern(),它首先检查String Pool中是否有内容为abc的对象,发现有,则将String Pool中内容为abc的对象的赋给s1.intern()方法的返回值。对于s2.intern(),首先检查String Pool中是否有内容为abc的对象,发现有,则将String Pool中内容为abc的对象的地址赋给s2.intern()方法的返回值。因为两者返回的地址都指向同一个对象,所以,s1.intern() == s2.intern()的结果为true,,即是语句(9)结果为true。
因此,当执行完语句(7)(8)(9)后,它们的结果分别是false、true、true。
问题6:当执行完语句(13)(14) (15)(16)后,它们的结果分别是什么?
hello == "hello"引用hello指向的对象就是String Pool中的“hello”,即语句(13)的结果为true。
hello == "hel" + "lo"当加号两边都是常量值时,就会组成一个新的常量值"hello"在String Pool里面,如果String Pool已经有相同内容的就不会再创建,则直接返回String Pool里面的内容为"hello"的字符串对象的内存地址,所以,hello == "hel" + "lo"结果为true。
hello =="hel" + lo 当加号两边有一个不是常量值,会在堆里面创建一个新的"hello"对象,一个在String Pool中,一个在Heap中,故输出false 。
hel + lo 同上,输出false。
因此,当执行完语句(7)(8)(9)后,它们的结果分别是true、true、false、false。
例程7:
Java代码
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
s2.intern();//虽然执行了s2.intern(),但它的返回值没有赋给s2
s3 = s3.intern();//把String Pool中“abc”的引用赋给s3
System.out.println(s1 == s2);
System.out.println(s1 == s2.intern());
System.out.println(s1 == s3);
[java]
view plaincopyprint?
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
s2.intern();//虽然执行了s2.intern(),但它的返回值没有赋给s2
s3 = s3.intern();//把String Pool中“abc”的引用赋给s3
System.out.println(s1 == s2);
System.out.println(s1 == s2.intern());
System.out.println(s1 == s3);
Java代码 String s1 = "abc"; String s2 = new String("abc"); String s3 = "a" + new String("bc"); System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3); [java] view plaincopyprint? String s1 = "abc"; String s2 = new String("abc"); String s3 = "a" + new String("bc"); System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3); String s1 = "abc"; String s2 = new String("abc"); String s3 = "a" + new String("bc"); System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s2 == s3);
运行结果如下:
false
false
false
结果说明:
s1指向String Pool中的字符串对象“abc”,编译期确定;
s2指向Heap中的字符串对象“abc”,运行期确定;
s3指向Heap中的另一个字符串对象“abc”,运行期确定。
注意:String s3 = "a" + new String("bc");等价于
String s3 = new StringBuilder().append("a").append(new String("bc")).toString();
思考:String s = "a" + new String("b") + "c";产生了那几个对象?
解:等价于String s = new StringBuilder().append("a").append(new String("b")).append("c").toString(),会在String Pool中产生"a"、"b"、"c"三个对象,在Heap中产生"b"、"abc"两个个对象。一共5个字符串对象。
例程9:
Java代码
String s1 = "Hello";
s1 = "Java";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s2 == s3);
[java]
view plaincopyprint?
String s1 = "Hello";
s1 = "Java";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s2 == s3);
String s1 = "Hello";
s1 = "Java";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2);
System.out.println(s2 == s3);
运行结果如下:
false
false
分析这段程序的执行过程:
首先在加载Java程序时,JVM会创建一片的内存空间(String Pool)专门存入string对象。
String s1 = "Hello",现在栈中创建一个字符串引用s1,然后JVM会在String Pool中查找是否存在"Hello",如果存在,则直接使用它,将其地址赋给s1,如果不存在(这时String Pool中显然不存在"Hello"),则在String Pool中创建"Hello",并将其地址赋给s1。
s1 = "Java",JVM会在String Pool中查找是否存在"Java",如果存在,则直接使用它,将其地址赋给s1,如果不存在(这时String Pool中显然不存在"Java"),则在String Pool中创建"Java",并将其地址赋给s1。而原来的字符串对象"Hello"仍然在String Pool中,没有消失,因为String对象的值是不能被修改的。这里只是改变了引用的值即引用指向的对象的地址,而没有改变它所引用的对象。
String s2 = "Hello",JVM会在String Pool里查看有没有字符串"Hello",若有,则返回它的地址给s2,否则,创建新的String对象"Hello", 放到String Pool里。这里由于"Hello"对象已经创建,并存在于String Pool中,因而不需要重新创建String对象"Hello"。此时s1指向String Pool中的"Java",s2指向String Pool中的"Hello",故s1 == s2的值为false。
String s3=String("Hello"),JVM会在String Pool里查看有没有字符串"Hello",若有,直接执行new操作,若没有,则先要在String Pool中创建"Hello",然后执行new操作,由于遇到了new,还会在Heap上(不是String Pool里)创建string对象"Hello",并将Heap上的"Hello"对象的地址赋给引用s3。所以s2 == s3将返回false,因为s2和s3不是引用同一个对象。
以上小结如理解有误,希望各位高手指教!
相关文章推荐
- 解析Java中创建String对象过程中的内存分配
- 关于创建String对象过程的内存分配
- 创建String对象过程的内存分配
- 关于创建String对象过程的内存分配
- 关于创建String对象过程的内存分配
- 创建String对象过程的内存分配小结
- 学习笔记之 equals() vs. == 和String对象的2种创建过程
- String对象的创建过程中的内存分配
- 学习笔记之 equals() vs. == 和String对象的2种创建过程
- 深入内存分析String对象的创建过程
- 创建string 对象的内存分配
- 关于创建String对象过程的内存分配
- 继承原则与子父类初始化过程,类成员访问权限,普通类与抽象类,throwble类,重写与重载,string对象修改,i=i++;
- C++类对象创建过程(分配空间、赋值和初始化、对象初始化顺序、虚函数表指针)
- Stringstr=new String("abc") 这行代码究竟创建了几个对象?
- 到底创建了几个String对象?
- 【转】关于Java String对象创建问题解惑
- 【知了堂学习笔记】String的创建过程—第一章
- String str=new String("abc");创建了几个String对象?
- String 创建几个对象问题(详解-不容错过)