在看《深入理解Java虚拟机》一书的“方法区和运行时常量池溢出”部分时,发现书中(P57)有这么一段代码:
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);
}
}
在JDK1.6中,如上代码返回的是
false
false
在JDK1.7中,如上代码返回的是
true
false
首先解释一下String.inter()这个方法的作用
在JDK1.6版本中:
String的intern()方法会得到字符串对象在常量池中和它相等的字符串的引用,如果是首次遇到的字符串,会把字符串实例复制到方法区中的永久代中,并返回永久代中这个字符串实例的引用
所以,str1.inter()返回的是常量池中的字符串实例引用,而str1指向的是Java堆中由StringBuilder创建的字符串实例引用,两者必然不相同
而在JDK1.7之后:
String的intern()方法不会再复制实例,只是在常量池中记录首次出现的实例引用
所以,str1.inter()返回的是Java堆中由StringBuilder创建的字符串实例引用,与str1一致
书中的解释是
对str2比较返回false是因为“java”这个字符串在执行StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则
嗯?"java"这个字符串什么时候出现过了呢?
jvm启动到执行我们的main方法之前会加载一系列的class,这当中的class所出现的字符串常量都已经被加载到常量池中了
所以,"java"这个字符串恰好已经在别的class出现,加到常量池中啦
我试了一下,例如"int","double"等字符串也会出现相同的情况
在尝试的过程中,我编写了如下代码:
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);
String str3 = new StringBuilder("计算机科学").toString();
System.out.println(str3.intern() == str3);
}
}
上述代码返回的结果是:
true
false
false
咦?为什么str3也返回false呢?难道“计算机科学”这个字符串也提前出现了?没有这么巧吧!?
又随便输了几个字符,str3返回的始终是false,这是怎么回事呢?
原来
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1);
这段代码等价于
String x = "计算机"
String y = "软件"
String str1 = new StringBuilder(x).append(y).toString();
System.out.println(str1.intern() == str1);
所以,先出现的字符串是"计算机"和"软件"这两个字符串,执行到StringBuilder().toString()时,常量池中是没有"计算机软件"这个字符串的,属于首次出现
而
String str3 = new StringBuilder("计算机科学").toString();
System.out.println(str3.intern() == str3);
这段代码等价于
String x = "计算机科学"
String str3 = new StringBuilder(x).toString();
System.out.println(str3.intern() == str3);
看明白了吧,"计算机科学"这个字符串已经提前被加到常量池中啦
相当于str3.intern()指向的是常量池中的字符串实例引用,而str3指向的是Java堆中的实例引用