为什么在JDK 8和JDK 9下,String.intern()返回不同的结果?

6
以下代码在使用JDK 8和JDK 9运行时会产生不同的结果。
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));
}

在JDK 8(版本1.8.0_172)以下,该代码会输出:
false
true
true

但是使用JDK 9(版本9.0.1)时,代码返回:

false
false
true

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


我在Java-12中进行了检查,它的输出与Java-8相同。 - dkb
2
当你说s3.intern()时,你实际上是指s3 = s3.intern()吗? - Joe C
2
你为什么在意呢?在你的代码中通常不应该使用intern()new String(String)。虽然在学术上很有趣,但对于大多数使用Java的方式来说没有实际用途。 - Mark Rotteveel
1个回答

8
结果取决于在调用 s3.intern() 之前字符串 "11" 是否已经存在于字符串池中。
如果不存在,s3.intern() 将把 s3 添加到池中并返回 s3。在这种情况下,s4 也将被赋予 "11" 的规范表示,因为它是使用字符串字面量初始化的。因此 s3==s4 将为 true。
如果存在,则 s3.intern() 将返回 "11" 的规范表示,这与 s3 不是同一个实例。因此 s3==s4 将为 false。
我没有 JDK9 版本来测试您的代码,但如果这是您得到的输出,则意味着在 JDK9 源代码中,在您的 main 方法执行之前,出现了 "11" 字符串字面量,这将添加该字符串到池中。
在 JDK8 中不是这样的。
您对 "1" 字符串的测试在两种情况下都返回 false,因为当您将其传递给 String 构造函数时,字符串 "1" 被添加到池中。因此,s.intern() 不会将由 s 引用的字符串添加到池中,而 String s2 = "1"; 是一个不同的实例。
当尝试理解此行为时,可以使用 intern 的 Javadoc:
String java.lang.String.intern() 返回字符串对象的规范表示。 类 String 私有地维护着一个最初为空的字符串池。 当调用 intern 方法时,如果池已经包含一个与此 String 对象相等的字符串(由 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回对此 String 对象的引用。 ... 所有文字字符串和字符串常量表达式都被 intern 化。

2
猜得不错。在Java 9中测试时,将字符串改为绝对胡言乱语而不是“11”,结果会返回false-true-true。 “11”必须已经存在于字符串池中。 - Michael
如果s3.intern()返回s3(这意味着在该调用之前池中没有"11"),那么String s4 = "11"也会返回s3(因为如果相等的字符串已经在池中,则字符串文字不会创建新实例)。如果"11"已经在池中,s3.intern()将返回与s3不同的实例(这将是与s4相同的实例)。因此我们将有s3!= s4 - Eran
1
罪魁祸首是 java.lang.VersionProps 中的字段 private static final java.lang.String VERSION_BUILD = "11";。这适用于问题中命名为 JDK 9.0.1 的版本,因为其完整版本为 9.0.1+11,也适用于版本 9.0.4+11 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接