为什么 String.intern() 在 Oracle JDK 1.7 中表现不同?

Why String.intern() behave differently in Oracle JDK 1.7?

这是一个 java 片段:

public class TestIntern {
    public static void main(String[] argvs){
        String s1 = new StringBuilder("ja").append("va").toString();
        String s2 = new StringBuilder("go").append("lang").toString();
        System.out.println(s1 == s1.intern());
        System.out.println(s2 == s2.intern());
    }
}

并且根据不同的JDKs

表现不同

在 Oracle JDK 1.7 中输出是:

false
true

在OpenJDK 1.6输出也是:

false
true

但在 Oracle JDK 1.6 中输出是:

false
false

因为此 String#intern 方法的 JavaDoc 指示

 * When the intern method is invoked, if the pool already contains a
 * string equal to this <code>String</code> object as determined by
 * the {@link #equals(Object)} method, then the string from the pool is
 * returned. Otherwise, this <code>String</code> object is added to the
 * pool and a reference to this <code>String</code> object is returned.
                           ~~~~
                             And what does *this* here mean? the string object
                             in the heap or in the string pool? if it returns 
                             object in the heap the out put should be:

                             true
                             true
                             otherwise should *always* be:

                             false
                             false
                             Am I right?

输出:

true
true

应该是预期的,但三个 JDK 都没有产生这个。为什么 Oracle JDK1.6 给出:

false
false

结果是?

我认为在OracleJDK 1.7 和openJDK 1.6 中,字符串池中必须有一些reserved 字符串,它们是什么?是否有文档指定所有 reserved 字符串? 真是一头雾水。

是否 s1.intern() returns s1 或其他一些 String 对象取决于它在驻留字符串池中找到的内容。如果池中已经有其他 String 对象,intern() 将 return 那个对象;否则它会将 s1 引用的 String 对象放入池中,并 return 引用相同的对象。

问题不在于不同的 Java 版本表现不同,而是当您 运行 测试时池碰巧包含不同的东西。 Oracle JDK 1.7 和 openJDK 1.6 中的池碰巧已经包含字符串 "java" 而不是字符串 "golang".

总算把本期的困惑都弄明白了,做个总结吧。

这是与@Ted 在他 Post 中的回答的互补回答。 他指出,正是程序在字符串池中找到的内容影响了 s1 == s1.intern() returns 的结果。它完美地解释了 Oracle JDK 1.7 和 OpenJDK 的行为,但没有解释 Oracle JDK 1.6 的奇怪行为,它总是 return false,不管 s1 是什么。

我想为什么 Oracle JDK 1.6 总是 return false 是因为这个 internal string pool was in the permanent generation but was transferred to heap 在 JDK 1.7。所以在JDK 1.6 中,堆上的字符串对象和永久代中的对象永远不会是同一个对象。

为了证明这一点,我在 Oracle JDK 1.6

下编写了一个 java 程序并 运行 它
import java.io.*;

public class TestIntern {
    public static void main(String[] argvs){
       String s1 = null; // string inputed
       String s2 = null; // string retrieved by string.intern() in this loop
       String s3 = null; // string retrieved by string.intern() in last loop
        while (true){
            System.out.println("Enter the string");
            BufferedReader br = new BufferedReader(
                                     new InputStreamReader(System.in));

            try {
                s1 = br.readLine();
            }catch (IOException ex){
                System.out.println("IOException caught, just exit");
                System.exit(1);
            }

            s3 = s2;          //<- s3 is referring to  the string obj
                              //which retrieved from pool by last exec of loop

            s2 = s1.intern(); //<- s2 now referring to the string 
                              //obj retrieved from the string pool

            // is s1 == s2 ? // this loop
            if (s1 == s2){
                System.out.println("s1 == s2 they are same string obj");

            }else{
                System.out.println("s1 != s2 they are NOT same string obj");
            }

            // is s2 == s3 ? compared from this loop retrieved(s2) 
            //                               and last loop retrieved(s3)
            if(s2 == s3){
                System.out.println("s2 == s3 they are same string obj");
            }else{
                System.out.println("s2 != s3 they are NOT same string obj");
            }
        }
    }
}

我们运行这个无限循环函数两次来计算s2和s3是否相同,当我们 使用来自 stdin 的相同字符串作为输入。

显然,当我们在循环中输入两次 aaa 时,s2 和 s3 指的是同一个字符串:

-> ~/Downloads/jdk1.6.0_45/bin/java TestIntern 
Enter the string
aaa
s1 != s2 they are NOT same string obj
s2 != s3 they are NOT same string obj
Enter the string
aaa
s1 != s2 they are NOT same string obj
s2 == s3 they are same string obj
Enter the string

这意味着,string.inter() 确实将 aaa 添加到字符串池和 return 对象,而不是 br.readLine() 安装的堆中的字符串对象, 这个 returned 对象是永久代中的对象。它们不一样。