String.intern() 的 return 解释

The return of String.intern() explained

考虑:

String s1 = new StringBuilder("Cattie").append(" & Doggie").toString();
System.out.println(s1.intern() == s1); // true why?
System.out.println(s1 == "Cattie & Doggie"); // true another why?

String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2); // false

String s3 = new String("Cattie & Doggie");
System.out.println(s3.intern() == s3); // false
System.out.println(s3 == "Cattie & Doggie"); // false

我很困惑为什么它们会因 String.intern()returned 值 而产生不同结果,它说:

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

特别是在这两个测试之后:

assertFalse("new String() should create a new instance", new String("jav") == "jav");
assertFalse("new StringBuilder() should create a new instance",
    new StringBuilder("jav").toString() == "jav");

我曾经读过 post 谈到一些 special strings 先于其他一切实习,但现在真的很模糊。

如果有一些字符串 pre-interned,有没有办法得到它们的列表?我只是好奇它们会是什么。


已更新

感谢@Eran 和@Slaw 的帮助,我终于可以为输出解释刚刚发生的事情

true
true
false
false
false
  1. 由于池中不存在"Cattie & Doggie",s1.intern() 会将当前对象引用放入池和return 本身,因此s1.intern() == s1
  2. "Cattie & Doggie" 现在已经在池中,所以字符串文字 "Cattie & Doggie" 将只使用池中的引用,实际上是 s1,所以我们又得到了 true
  3. new StringBuilder().toString() 将创建一个新实例,而 "java" 已经在池中,然后池中的引用将在调用 s2.intern() 时被 returned,所以 s2.intern() != s2 我们有 false;
  4. new String() 也会 return 一个新实例,但是当我们尝试 s3.intern() 时,它会 return 之前存储在池中的引用实际上是 s1 所以 s3.intern() != s3 我们有 false;
  5. 正如 #2 已经讨论过的,字符串文字 "Cattie & Doggie" 将 return 已经存储在池中的引用(实际上是 s1),所以 s3 != "Cattie & Doggie" 并且我们又来了false

感谢@Sunny 提供获取所有 interned 字符串的技巧。

s2.intern() 将 return 由 s2 引用的实例,仅当字符串池不包含 String 且其值为 "java" 之前那个电话。 JDK 类 在你的代码被执行之前实习一些 Strings。 "java" 必须是其中之一。因此,s2.intern() return 是之前驻留的实例,而不是 s2

另一方面,JDK 类 没有实习任何值等于 "Cattie & Doggie" 的 String,所以 s1.intern() returns s1.

我不知道有任何预留存字符串列表。这样的列表很可能会被视为实现细节,它可能因不同的 JDK 实现和 JDK 版本而异,不应依赖。

当在 String 对象上调用 intern() 方法时,它会在池中查找此 String 对象包含的字符串,如果在那里找到该字符串,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对此 String 对象的引用。

所以 java 字符串必须已经在池中。因此它给出了错误。

您可以打印池中的所有字符串

How to print the whole String pool?

如果您使用 openjdk,这里有一个 example 来获取所有字符串。

字符串文字(像 "a string" 这样硬编码的文字)已经由编译器为您保留。但是那些以编程方式获取的字符串不是,并且只有在您使用 .intern() 方法时才会被保留。

通常你不会手动实习字符串,除非你知道你会在内存中存储大量重复的字符串,这样你可以节省很多内存。

这里有解释: What is Java String interning?