intern只在常量池里记录首次出现的实例引用
来看一段代码
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
}
}
这段代码在JDK 6中运行,会得到两个false,而在JDK 7中运行,会得到一个true和一个false。
产生差异的原因是,在JDK 6中,intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储,返回的也是永久代里面这个字符串实例的引用,而由StringBuilder创建的字符串对象实例在Java堆上,所以必然不可能是同一个引用,结果将返回false。
而JDK 7(以及部分其他虚拟机,例如JRockit)的intern()方法实现就不需要再拷贝字符串的实例 到永久代了,既然字符串常量池已经移到Java堆中,那只需要在常量池里记录一下首次出现的实例引 用即可,因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返 回false,这是因为“java” [2]这个字符串在执行String-Builder.toString()之前就已经出现过了,字符串常量 池中已经有它的引用,不符合intern()方法要求“首次遇到”的原则,“计算机软件”这个字符串则是首次 出现的,因此结果返回true。
这个intern()的用法确实与我们想象的不太一样
下面是我的测试用例,看完可以帮助更好的理解
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
String A = "计算机软件";
String B = new String("计算机软件");
//此时B首次出现在字符串常量池的地址就是A,因此返回true
System.out.println(A == B.intern());
String str1 = new StringBuilder("计算机").append("软件").toString();
//此时str1首次出现的位置是与A相同的,A的地址与str1不同,因此返回false。
//但是如果我们在上文中不定义A,那么这个判断就会返回true
System.out.println(str1.intern() == str1);
//验证str1与A的地址在字符串常量池中是相同的,输出true
System.out.println(A == str1.intern());
//B在字符串常量池中的地址也与A的地址相同,也与str1.intern相同,返回true
System.out.println(B.intern() == str1.intern());
String str2 = new StringBuilder("ja").append("va").toString();
//这个是因为在我们看不到的地方,java这个字符串就已经存在了,第一次的地址不知道,因此返回了false
System.out.println(str2.intern() == str2);
//我们再来看一个例子,此时输出的就是true了,因为”学习计算机“这个字符串第一次出现在字符串常量池,两个地址是相同的
String D = new StringBuilder("学a习").append("计算机").toString();
System.out.println(D.intern() == D);
}
}
输出结果如图