8

我阅读了这个关于 Java 的字符串池的问题 并了解了字符串池的基本概念,但仍然不了解其行为。

首先:如果您直接分配值并且 s1 和 s2 都引用池中的同一个对象,则它可以工作

String s1 = "a" + "bc";
String s2 = "ab" + "c";
System.out.println("s1 == s2? " + (s1 == s2));

但是如果我更改字符串 s1+="d",那么池应该有一个字符串对象“abcd”?那么当我更改 s2+="d" 时,它应该在池中找到字符串对象“abcd”并将该对象分配给 s2?但事实并非如此,它们也没有引用同一个对象。这是为什么?

String s1 = "abc";
String s2 = "abc";
System.out.println("s1 == s2? " + (s1 == s2));

s1 += "d";                  
s2 += "d";
System.out.println("s1 == s2? " + (s1 == s2));
4

6 回答 6

7

当您调用一个字符串时,保证会汇集String.intern()字符串。

String s1 = "abcd".intern();
String s2 = "abc";
s2 += "d";
s2 = s2.intern();
s1 == s2 // returns true

当编译器看到一个常量时,它足够聪明地优化和汇集字符串文字,即:

String s1 = "abcd";
String s2 = "abcd";
s1 == s2 // returns true

Java 语言规范指出:

每个字符串文字都是对 String 类(第 4.3.3 节)实例(第 4.3.1 节、第 12.5 节)的引用(第 4.3 节)。字符串对象有一个常量值。字符串字面量——或者更一般地,作为常量表达式值的字符串(第 15.28 节)——是“内部的”,以便使用 String.intern 方法共享唯一的实例。

所以在 的情况下s2 += "d",编译器没有你那么聪明,只是汇集了"d"

于 2013-01-23T21:51:56.757 回答
3

我不确定这一点,所以这几乎是猜测,但我怀疑第一个示例中可能存在一些编译器技巧(它是内联的并且很明显发生了什么),但它不够聪明在第二个例子中它关闭了(它不是那么明显)。

如果我是对的,编译器要么在编译"a" + "bc"时看到并简单地将其压缩到,"abc"要么它看到这两行并汇集字符串,因为它意识到它们将被使用。我赌的是前者。。

并非所有字符串都必须合并。

于 2013-01-23T21:55:58.547 回答
2

See the documentation for String#intern(). The last line there states:

All literal strings and string-valued constant expressions are interned.

Your += example is neither a literal string nor a string-valued constant expression, so it is not put in the String pool.

于 2013-01-23T22:05:08.200 回答
2

The compiler can perform constant evaluation but not in the case where you modify the values

Try instead following and see what happens if you drop final from either variable.

final String s1 = "abc";
final String s2 = "abc";
System.out.println("s1 == s2? " + (s1 == s2));

String s3 = s1 + "d";                  
String s4 = s2 + "d";
System.out.println("s3 == s4? " + (s3 == s4));
于 2013-01-23T22:05:36.277 回答
0

这是我的猜测:

字符串 s1 = "a" + "bc"; 字符串 s2 = "ab" + "c";

我认为这是编译时间,它们被确定为生成相同的字符串,因此只为两者制作了一个对象。

但是当你给它们都加上“d”时,这两个字符串是分开完成的(因为它是在实时完成的,可能会有异常中断它等等,所以它不能预先做)等等不会自动让它们引用一个对象。

于 2013-01-23T21:55:45.783 回答
0

我认为这里发生的是: 1. for String s1 = "a" + "bc"; 字符串 s2 = "ab" + "c"; Java 编译器足够聪明,知道 s1 和 s2 的字面值相同,因此编译器将它们指向字符串池中的相同字面值

  1. 对于 s1 += "d";
    s2 += "d";

编译器无法知道 s1 和 s2 是否最终会成为相同的值,在运行时,除非您调用 String.intern(),否则 jvm 不会检查字符串文字池以查看该值是否已经存在。

于 2013-01-23T22:11:59.093 回答