为什么在JDK8和JDK9下String.intern()返回不同的结果?

时间:2018-11-04 13:28:55

标签: java

在JDK8和JDK9下,以下代码具有不同的结果。

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    //String s3 = "1" + "1";
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
    System.out.println(s3.equals(s4));
}

在JDK8(“ 1.8.0_172”版本)下,代码返回:

false true true

但在JDK9(“ 9.0.1”版本)下,代码返回:

false false true

我检查了两个JDK版本,它们是正确的。为什么代码的结果不同?我的程序有什么问题吗?

2 个答案:

答案 0 :(得分:6)

结果取决于在调用s3.intern()之前字符串池中是否已经有字符串“ 11”。

如果不是,s3.intern()会将s3添加到池中并返回s3。在那种情况下,s4也将被指定为“ 11”的规范表示形式,因为它是用String文字初始化的。因此s3==s4将是true

如果是,则s3.intern()将返回规范表示形式“ 11”,它与s3实例不同。因此s3==s4将是false

我没有用于测试代码的JDK9版本,但是如果得到的是输出,则表明在main之前执行的JDK9源代码中的某处出现了“ 11 “ String文字,它将String添加到池中。

在JDK8中不是这种情况。

在两种情况下,使用“ 1” String进行的测试都会得到false,因为当您将String“ 1”传递给{{1}时,会将其添加到池中} String中的构造函数。因此,new String("1")不会将s.intern()引用的String添加到池中,并且s是与String s2 = "1";不同的实例。

尝试了解此行为时,s的Javadoc很方便:

  

字符串java.lang.String.intern()

     

返回字符串对象的规范表示。

     

最初为空的字符串池由String类私有维护。

     

调用intern方法时,如果池已经包含等于equals(Object)方法确定的此String对象的字符串,则返回池中的字符串。否则,将此String对象添加到池中,并返回对此String对象的引用。

     

...

     

所有文字字符串和字符串值常量表达式均已插入。

答案 1 :(得分:0)

伊兰的答案很接近。正如他所确定的,关键问题是何时字符串常量被插入。

根据我的权威,最近的一些JVM懒惰地进行String文字的中间化,并且它发生在第一次使用该文字之前。告诉这个人的人说这是/是新的行为。

因此,实际的解释似乎是Java 9是在引入这种新的“懒惰”行为时开始的,这解释了OP观察到的Java 8与Java 9之间的区别。

(我将尝试查看OpenJDK源代码中发生的情况...。)