Java String及intern

用StringBuffer生成字符串过程中,不同的拼接方式会影响最终字符串对象的位置。

总结:

字符串对象通过字面值直接生成时则直接对应常量池对象。

如果每个字符串都是通过两个及其以上的小的字符串通过StringBuffer对象拼接而成,并且没有全由字面值生成的字符串对象,则第一个生成的字符串对应常量池中的该字符串对象。

如果生成的字符串对象中有由一个完整字符串或者一个完整字符串拼接空串通过StringBuffer对象生成,并且没有全由字面值生成的字符串对象,则所有的字符串对象均不对应常量池中的对象


Q:String s = new String(“xyz”),创建了几个String Object?

A:两个,常量池中的”xyz”和堆中对象。

Q:下列程序的输出结果:

String s1 = “abc”;

String s2 = “abc”;

System.out.println(s1 == s2);

A:true,均指向常量池中对象。

Q:下列程序的输出结果:

String s1 = new String(“abc”);

String s2 = new String(“abc”);

System.out.println(s1 == s2);

A:false,两个引用指向堆中的不同对象。

Q:下列程序的输出结果:

String s1 = “abc”;

String s2 = “a”;

String s3 = “bc”;

String s4 = s2 + s3;

System.out.println(s1 == s4);

A:false,因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。

Q:下列程序的输出结果:

String s1 = “abc”;

final String s2 = “a”;

final String s3 = “bc”;

String s4 = s2 + s3;

System.out.println(s1 == s4);

A:true,因为final变量在编译后会直接替换成对应的值,所以实际上等于s4=”a”+”bc”,而这种情况下,编译器会直接合并为s4=”abc”,所以最终s1==s4。

Q:下列程序的输出结果:

String s = new String(“abc”);

String s1 = “abc”;

String s2 = new String(“abc”);

System.out.println(s == s1.intern());

System.out.println(s == s2.intern());

System.out.println(s1 == s2.intern());

A:false,false,true


String str1 ="string";

String str2 =newString("string");

String str3 = str2.intern();

System.out.println(str1==str2);//#1

System.out.println(str1==str3);//#2

结果 #1:因为str1指向的是字符串中的常量,str2是在堆中生成的对象,所以str1==str2返回false。

结果 #2:str2调用intern方法,会将str2中值(“string”)复制到常量池中,但是常量池中已经存在该字符串(即str1指向的字符串),所以直接返回该字符串的引用,因此str1==str2返回true。


String baseStr ="baseStr";

finalString baseFinalStr ="baseStr";

String str1 ="baseStr01";

String str2 ="baseStr"+"01";

String str3 = baseStr +"01";

String str4 = baseFinalStr+"01";

String str5 =newString("baseStr01").intern();

System.out.println(str1 == str2);//#3

System.out.println(str1 == str3);//#4

System.out.println(str1 == str4);//#5

System.out.println(str1 == str5);//#6

结果#3:str1==str2 肯定会返回true,因为str1和str2都指向常量池中的同一引用地址。所以其实在JAVA 1.6之后,常量字符串的“+”操作,编译阶段直接会合成为一个字符串。

结果 #4:因为str3实际上是stringBuilder.append()生成的结果,所以与str1不相等,结果返回false。

结果 #5 :因为str1和str4指向的都是常量池中的第三项,所以str1==str4返回true。这里我们还能发现一个现象,对于final字段,编译期直接进行了常量替换,而对于非final字段则是在运行期进行赋值处理的

结果 #6 :因为str5和str1都指向的都是常量池中的同一个字符串,所以str1==str5返回true。


String str2 =newString("str")+newString("01");

str2.intern();

String str1 ="str01";

System.out.println(str2==str1);//#7


String str1 ="str01";

String str2 =newString("str")+newString("01");

str2.intern();

System.out.println(str2 == str1);//#8

根据对代码段一的分析,应该可以很简单得出 JDK 1.6 的结果,因为 str2 和 str1本来就是指向不同的位置,理应返回false。

比较奇怪的问题在于JDK 1.7后,对于第一种情况返回true,但是调换了一下位置返回的结果就变成了false。这个原因主要是从JDK 1.7后,HotSpot 将常量池从永久代移到了元空间,正因为如此,JDK 1.7 后的intern方法在实现上发生了比较大的改变,JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。所以:

结果 #7:在第一种情况下,因为常量池中没有“str01”这个字符串,所以会在常量池中生成一个对堆中的“str01”的引用,而在进行字面量赋值的时候,常量池中已经存在,所以直接返回该引用即可,因此str1和str2都指向堆中的字符串,返回true。

结果 #8:调换位置以后,因为在进行字面量赋值(String str1 = “str01″)的时候,常量池中不存在,所以str1指向的常量池中的位置,而str2指向的是堆中的对象,再进行intern方法时,对str1和str2已经没有影响了,所以返回false。

你可能感兴趣的:(Java String及intern)