今天在 Reeder 上看到一个关于 String Intern 的文章,自己测试了一下,发现有些地方并不严谨。

  原文用了一段如下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}

  目的很明确,希望通过==判断 intern 能否为下一次赋值从 intern 的池里提供。然而这段代码做了一个假设:在 main 函数之前,没有任何字符串被 intern。

  然而我在Java8以上测试,却发现了有意思的情况。考虑以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    public static void main(String[] args) {
        String s =  String.valueOf(1) + String.valueOf(1);
        s.intern();
        String s2 = "11";
        System.out.println(s == s2);

        String s3 = String.valueOf(1) + String.valueOf(2);
        s3.intern();
        String s4 = "12";
        System.out.println(s3 == s4);
    }

  按照原文作者的假设,结果应该都是 true 才对,第一次 intern 的时候该对象保存进入常量,第二次赋值的时候取出,因此==的两个结果是 true。

  但是在我的机器和其他同事的机器上却发现有人是 false,true,有人是 false,false,有人是 true,true。也就是说,JVM 根据某些信息提前进行了 intern,这种 intern 操作来自哪里,我暂时还不知道,有知道的同学可以帮忙在回复里解释一下。

  BTW,调用 intern 却不是用其返回值作为对象,那么这次使用的对象不一定是在 Intern 里存储的那个对象,使用==的结果并不是确定的。这种类似 C 的未定义行为,不能作为 Java 特性的说明,也不能在生产代码里使用,原文的原理是正确的,但是代码却没有说服力了。这从另一个角度提醒我们写代码还是要大胆假设,小心求证。