测试环境:eclipse+JDK 1.8
测试例子:
public static void main(String[] args){
String s1=new String("a")+new String("b"); //1
System.out.println(s1.intern()==s1); //2
String s2=new String("a")+new String("b"); //3
System.out.println(s2.intern()==s2); //4
}
JDK 1.6 输出结果
false
false
JDK 1.7(1.8) 输出结果
true
false
引自周志明《深》解释:
在JDK 1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串实例的引用;而JDK 1.7(以及部分虚拟机)的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用。
两个图帮助理解:
JDK 1.6
- 执行第一句后,字符串常量池将会产生字符串“a”和“b”以及它们的引用。在堆区创建一个String对象,并将引用返回给s1。
- 执行第二句s1.intern()方法后,在JDK 1.6中,先将字符串“ab”复制到字符串常量池,然后创建字符串“ab”的引用并返回给si.intern()。此时,明显s1和s1.intern()指向的对象不同,返回false。
- 第三和第四句类似,值得注意的是,此时如果执行
s1.intern()==s2.intern()
将会返回true。
JDK 1.7(1.8)
注意1.7之后,字符串常量池迁移到堆区。
- 执行第一句,与JDK 1.6相同
- 执行第二句s1.intern()方法时,发现字符串常量池中并没有字符串“ab”的引用,此时不再在字符串常量池中创建“ab”字符串,而是在字符串常量池中创建“ab”字符串的引用,然后指向首次出现的实例对象,也即是s1所指向的对象,所以
s1.intern()==s1
返回true。 - 执行第四句s2.intern()方法时,发现字符串常量池中已经存在字符串“ab”的引用,直接指向该引用。此时如果执行
s2.intern()==s1.intern()
或者s2.intern()==s1
都会返回true。
为了验证以上所述,再进行一次检验(JDK 1.8)
public static void main(String[] args){
String s1=new String("a")+new String("b"); //1
System.out.println(s1.intern()==s1); //2
s1=null;
System.gc();
String s2=new String("a")+new String("b"); //5
System.out.println(s2.intern()==s2); //6
}
运行结果
true
true
调用gc()方法,将s1指向的实例对象清除,所以执行到第6句时,字符串常量池中不存在“ab”字符串引用,之后的操作与执行1、2句相同。