为什么String.intern()在Oracle JDK 1.7中的行为有所不同?

时间:2015-01-07 04:59:27

标签: java string

这是一个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());
    }
}

根据不同的JDK,它的行为不同

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中,字符串池中必须有一些保留的字符串,它们是什么?是否有文档指定所有保留的字符串? 真的很困惑。

2 个答案:

答案 0 :(得分:5)

s1.intern()是否返回s1或其他String对象取决于它在实习字符串池中找到的内容。如果池中已经存在其他String个对象,intern()将返回该另一个对象;否则它会将String引用的s1对象放入池中并返回相同的对象。

问题不在于不同的Java版本表现不同,而是在运行测试时,池碰巧包含不同的内容。我发现Oracle JDK 1.7和openJDK 1.6中的池恰好已经包含字符串"java"而不是字符串"golang",这一点并不特别令人惊讶。

答案 1 :(得分:0)

最后,我想我已经弄清了这个问题的所有混淆,应该做一个总结。

这是@Ted回答的补充答案,在他的帖子中。 他指出,这是程序在字符串池中找到的影响s1 == s1.intern()返回结果的内容。它完美地解释了Oracle JDK 1.7和OpenJDK的行为,但不是Oracle JDK 1.6的奇怪行为,它始终返回false,无论s1是什么。

我认为Oracle JDK 1.6总是返回false的原因是因为内部字符串池在永久代中,而在JDK 1.7中是was transferred to heap。所以在JDK 1.6中,堆上的字符串对象和永久代中的对象永远不会是同一个对象。

为了证明这一点,我编写了一个java程序并在Oracle JDK 1.6下运行它

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添加到字符串池并返回对象,而不是由br.readLine()安装的堆中的字符串对象,此返回的对象是永久的一代。他们不一样。