预先抱歉提出一个基本问题,但有人可以解释一下原因:
String s = "lo";
String str7 = "Hel" + s;
String str8 = "He" + "llo";
System.out.println("str7 == str8 is " + (str7 == str8));
输出错误。 我认为str7和str8都指向String池中的同一个对象,因为字符串是不可变的。
我错了吗? str7和str8都不在池中但在堆中吗?为什么呢?
如果结果与字符串池中的不可变字符串完全相同,能否请您提供一些String操作的示例?
PS:
String str9 = "He" +"llo";
System.out.println("str8 == str9 is " + (str9 == str8));
输出true
答案 0 :(得分:4)
如果池中所有文字都进入字符串池,那么您的理解是正确的。
当你进行连接时会出现混乱。以下是需要注意的两点。
1)如果String在编译时解析,则是它与String池同步并使用相同的文字。对于前
String str7 = "Helllo";
String str8 = "He" + "llo";
请注意,两者都是普通文字。因此没有运行时转换等。
2)如果String在运行时解析,它会在运行时解析为一个新字符串,并且与其他任何其他字符串不同,除非您使用.equals方法来比较其内容。
String str7 = "Hel" + s;
String str8 = "He" + "llo";
System.out.println("str7 == str8 is " + (str7 == str8)); //false
在这种情况下,带有(+)运算符的concat字符串,JVM返回new StringBuilder(string...).toString()
,因为一个是普通文字而另一个是变量。
如果只有一个操作数表达式是String类型,则在另一个操作数上执行字符串转换(第5.1.11节)以在运行时生成字符串。
评论中的问题:
是否意味着如果字符串是作为文字串联产生的,那么结果总是与池中的字符串相同?
是的。记住,你的意思是编译时解析表达式(也就是常量表达式),而不是运行时。
当我们将字符串文字与字符串对象连接起来时,结果字符串总是一个新的字符串对象,它是通过引擎盖下的StringBuilder构建的?
是的,返回了一个新的String。附加的JVM链接确认了这一点。
答案 1 :(得分:2)
String s = "lo";
String str7 = "Hel" + s;
String str8 = "He" + "llo";
String str9 = "He" + "llo";
s
引用的对象是String对象,表示" lo"文字。它在字符串池中。
str8
和str9
引用的对象是评估作为常量表达式的表达式的结果。因此它们位于字符串池中。事实上,由于表达式评估为"相同"字符串,str8
和str9
引用相同的实际String
对象。
str7
引用的对象是评估不是常量表达式的表达式的结果。因此它不在字符串池中。
此处"Hel" + s
不是常量表达式的最终原因是s
未声明为final
。
要记住的事情这里是String对象只在两种情况下在String池中分配:
String.intern()
方法明确生成它们。字符串文字是常量表达式的子案例。
有关常量表达式的详细说明,请参阅Java语言规范 - JLS 15.28(常量表达式)和JLS 4.12.4(常量变量)。
在其中一种情况下不在字符串池中生成的任何字符串。
答案 2 :(得分:1)
str7
和str8
都在池中,但它们不是同一个字符串。也就是说,它们是相同字符序列的不同版本。
考虑一下,如果每次创建字符串时VM必须扫描整个池,以查看是否已存在具有相同字符序列的另一个字符串,那么您将获得的性能提升。
在您的新示例中,您正在从相同的基础字符串构建str8
和str9
,因此编译器可以更容易地告诉每个字符串的结果是相同的并且可以重用该条目在游泳池里。
答案 3 :(得分:0)
如果您调用String#intern()
,则编译器将扫描String
池并按您的预期执行。也就是说,
String s = "lo";
String str7 = ("Hel" + s).intern();
String str8 = ("He" + "llo").intern();
System.out.println("str7 == str8 is " + (str7 == str8));
输出
str7 == str8 is true
导致此行为的原因有两个
String
是不可变的,因此使用+
需要创建新的String
String
串联通常通过StringBuilder
实施 - new StringBuilder("Hel").append(s).toString()
和new StringBuilder("He").append("llo").toString()
且StringBuilder
不扫描{ {1}}实习生池。