这种行为是因为实习。String#intern
的文档描述了该行为(包括为什么它会显示在您的代码中,即使您从不调用):String#intern
字符串池(最初是空的)由类私下维护。String
调用该方法时,如果池中已包含与该方法确定的此对象相等的字符串,则返回池中的字符串。否则,此对象将添加到池中,并返回对此对象的引用。intern
String
equals(Object)
String
String
因此,对于任何两个字符串 和 ,是当且仅当 为 true。s
t
s.intern() == t.intern()
true
s.equals(t)
所有文本字符串和字符串值常量表达式都将被暂存。字符串文本在 Java 语言规范的 §3.10.5 中定义。
例如:
public class Test {
private String s1 = "Hi";
public static void main(String [] args) {
new Test().test();
System.exit(0);
}
public void test() {
String s2 ="Hi";
String s3;
System.out.println("[statics] s2 == s1? " + (s2 == s1));
s3 = "H" + part2();
System.out.println("[before interning] s3 == s1? " + (s3 == s1));
s3 = s3.intern();
System.out.println("[after interning] s3 == s1? " + (s3 == s1));
System.exit(0);
}
protected String part2() {
return "i";
}
}
输出:
[statics] s2 == s1? true
[before interning] s3 == s1? false
[after interning] s3 == s1? true
走过来:
- 分配给 的文字会自动暂存,因此最终会引用池中的字符串。
s1
s1
- 分配给 的文字也是自动暂存的,因此最终指向同一实例指向。即使这两段代码可能彼此完全未知,这也很好,因为Java的实例是不可变的。您无法更改它们。您可以使用诸如取回具有更改的新字符串之类的方法,但是您调用的原始字符串(等)保持不变。因此,它们可以安全地在不相关的代码之间共享。
s2
s2
s1
String
toLowerCase
toLowerCase
- 我们通过运行时操作创建一个新实例。即使新实例具有与实习实例相同的字符序列,它也是一个单独的实例。运行时不会自动插入动态创建的字符串,因为涉及成本:在池中查找字符串的工作。(而在编译时,编译器可以将该成本自行承担。所以现在我们有两个实例,一个指向,一个指向。因此,代码显示.
String
s1
s2
s3
s3 != s1
- 然后我们明确地实习.也许这是我们计划长期保留的一个大字符串,我们认为它可能会在其他地方复制。因此,我们接受实习它的工作,以换取潜在的内存节省。由于根据定义,实习意味着我们可以取回一个新的引用,因此我们将结果赋回给 。
s3
s3
- 我们可以看到,现在确实指向同一个实例并指向。
s3
s1
s2