有关String.intern()返回引用的疑惑

在看《深入理解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呢???

书中的解释是

对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堆中的实例引用

你可能感兴趣的:(Java,jvm,intern,java,常量池,String.intern())