- 先看如下代码输出结果
String str = new StringBuilder("11").append("va").toString();
System.out.println(str.intern() == str);
System.out.println("---------------------");
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
- 在jdk1.6中运行,会得到两个false。在jdk1.7之后运行,第一个true,第二个false.
因为在1.6中intern()会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中的这个新实例对象的引用。所以此时两者不相等。 - 在1.7之后,永久代开始移除(1.8中彻底移除永久代),JDK 1.7 和 1.8将字符串常量由永久代转移到堆中,此时intern实现不再复制实例,如果该实例在常量池中首次出现,只是在常量池中记录该实例引用,因此intern返回的引用和原来StringBuilder创建的字符串实例是同一个。对于第二个Str2,因为StringBuilder创建的最终创建的"java"已经在StringBuilder.toString之前在常量池中出现过,所以不是首次出现,intern就会直接返回在常量池中的实例引用,而StringBuilder创建的是在堆内存中新的实例,因此两者不想等。可以用如下方式证实
String str = new StringBuilder("11").append("va").toString();
System.out.println(str.intern() == str);
System.out.println("---------------------");
String str3 = new String("xxxs");
System.out.println(str3.intern() == str3);
- 输出:
true
---------------------
false
- 解析:
第一个返回true,解释如上。
第二个同样是new的一个字符串,StringBuilder.toString底层代码也是new一个对象,但为什么返回false呢。 因为在new String(“xxxs”)中,引号部分"xxxs"已经先在常量池中开辟空间存储了,然后再是在堆中存储,所以Str3.inter,不再是首次出现在常量池,因为返回的是常量池中实例的引用,自然和堆中的引用不同,因此是false.
注:可以打印这些字符串的真实的地址identityHashCode(不能取hashcode,因为String重写了hashcode方法),,identityHashCode相同的,两个对象就相等。
String str = new StringBuilder("11").append("va").toString();
// "11va"第一次在常量池中出现,只记录引用,不会copy实例(1.7之前的会copy)
System.out.println(System.identityHashCode(str.intern()));
System.out.println(System.identityHashCode(str));
System.out.println(str.intern() == str);
System.out.println("-------------");
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(System.identityHashCode(str2.intern()));
System.out.println(System.identityHashCode(str2));
System.out.println(str2.intern() == str2);
System.out.println("-------------");
String str3 = new String("xxxs");
// str3.intern()之前,"xxxs"已经定义在常量池中
System.out.println(System.identityHashCode(str3.intern()));
System.out.println(System.identityHashCode(str3));
// "xxxs"已经在常量池中存放,返回常量池中的引用
System.out.println(str3.intern() == str3);
System.out.println("-------------");
System.out.println(str3.intern() == "xxxs");
- jdk1.7和1.8运行结果如下:
31786283
31786283
true
-------------
11950696
32087476
false
-------------
7392722
30982214
false
-------------
true
- jdk1.6运行结果:
9023134
19336051
false
-------------
6336176
23899971
false
-------------
6718604
8918002
false
-------------
true
原文:
因为JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串的实例的引用,而StringBulder创建的字符串实例在Java堆上,所以必然不是同一个引用,将返回false。
在JDK1.7中,intern()的实现不会在复制实例,只是在常量池中记录首次出现的实例引用,因此返回的是引用和由StringBuilder.toString()创建的那个字符串实例是同一个。
附:java关键字位置: