字符串池对象

4
请问您能为我澄清一下以下情况将会创建多少个对象以及为什么吗?对此我还有点困惑。
String s1 = "cat";

String s2 = "cat";

String s3 = "c"+"at";

String s4 = new String("cat");

String s5 = "kitty"+"cat";

String s6 = new String("kittycat");

String s7 = s5;

String s8= new String(s5); // Newly Added in this Question

一个引发不同意见的好问题! - ZygD
@ZygD 幸运的是每个人都有自己的观点,所以我可以说这个问题对我来说显得有些懒惰。在SO上有很多类似的问题(EJP已经链接了一个),在研究过程中很容易找到答案。 - Tom
@Tom 可能是对的,但这个问题将其他问题中的小事情结合在一起。这就是为什么我真的很喜欢它。简洁明了。 - ZygD
2个回答

8

让我们逐步来看:

String s1 = "cat";
String s2 = "cat";

这两个常数池条目将由javac编译器在您的类文件中创建,它们将完全相同。当此类被加载时,此字符串(以及所有其他常量池字符串)将自动成为内部化的,因此它也将与其他类中的“cat”字符串合并。
String s3 = "c"+"at";

这几乎是一样的:如果在编译期间可以计算字符串拼接,则由javac处理。因此,它与 s1 和 s2 实际上是相同的。这在JLS中有所涵盖,第15.18.1章

除非表达式是常量表达式(§15.28),否则将新创建String对象(§12.5)。

String s4 = new String("cat");

在这里,您显式创建了一个新对象。Java语言保证当您使用new关键字时,您将拥有一个新的独立对象,它不能与先前创建的任何对象相同。但是,如果您仅在当前方法(或可以内联到当前方法中的方法)中使用此对象,并且不使用==操作将其与其他字符串进行比较,JIT编译器可以跳过对象分配以进行优化。但是,如果您实际使用==System.identityHashCode,或者该方法作为解释帧执行,则它将是实际的新对象。
传递给参数的"cat"字符串实际上是与s1s2s3相同的对象。
String s5 = "kitty"+"cat";

这与s3类似:javac编译器在编译期间只会创建一个"kittycat"字符串。查看字节码,您甚至无法知道源代码中是否有连接操作。

String s6 = new String("kittycat");

这与s4类似:显式创建了一个新对象。如果你后来尝试使用s6 == "kittycat",你会得到false

String s7 = s5;

在这里,你只是将一个引用分配给之前创建的字符串,因此这里不会创建新对象。
因此,答案是:最多会创建4个字符串,但在某些情况下可以优化到2个字符串。而且最重要的是:如果您尝试从同一程序内部检查有多少个字符串(即不使用Java代理或分析内存转储),您将始终获得四个。

2
所以,我的答案带有2,虽然收到了主要的打击,但实际上对于这个问题是正确的 :)。 - John
@user3360241:我没有看到你的答案。加上了最后一句话来澄清事情。 - Tagir Valeev
@user3360241 完全不是这样。你的回答在几个方面都是错误的。请再次阅读这个优秀的答案,了解原因。 - user207421
@Tagir Valeev:我不确定这是否正确。但是为了连接字符串s3="c"+"at",编译器会将"c"和"at"视为两个分别由s3引用的字符串对象吗? - Kumar
1
@Kumar,不会存在字符串“c”和“at”。编译后的.class文件中只会有字符串“cat”。 - Tagir Valeev

0

user3360241 - 你的回答似乎是正确的... 不确定为什么没有给出任何解释就被投票否决了... 如果你这样做,大小将会是两个...

Set<Integer> set = new HashSet<Integer>();

set.add(s1.hashCode());
set.add(s2.hashCode());
set.add(s3.hashCode());
set.add(s4.hashCode());
set.add(s5.hashCode());
set.add(s6.hashCode());
set.add(s7.hashCode());

System.out.println("size :: "+set.size());

1
s4.hashCode()替换为System.identityHashCode(s4),并观察发生了什么。 - Misha
2
@Misha:最好使用IdentityHashMap。虽然System.identityHashCode的冲突几率很低,但不能保证其唯一性。 - Tagir Valeev
明白了,谢谢您的解释! - TheCodingFrog

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