Java中的String concat和+运算符之间有什么区别吗?

20

重复

Java字符串连接

我很好奇这两者之间的区别是什么。

我理解字符串池的方式是这样的:

这将在字符串池中创建3个字符串对象,其中有2个的所有引用都丢失了。

String mystr = "str";
mystr += "end";

这不也会在字符串池中创建三个对象吗?

String mystr = "str";
mystr = mystr.concat("end")

我知道在需要进行大量字符串拼接时,StringBuilder和StringBuffer在内存使用方面要更加高效。但我只是好奇在内存使用方面,+运算符和concat方法之间是否有任何区别。


我认为这里只有两个字符串被内部化了;'str'和'end'。字符串'strend'没有被内部化,因为它不是一个字面量;你必须手动将其内部化。有人可以确认/否认吗? - Tim Frey
请查看我在完全重复的问题中的回答:https://dev59.com/JnVD5IYBdhLWcg3wOo9h - Tom Hawtin - tackline
在生成的代码中,我没有看到任何对intern的调用,虽然我没有查看concat的源代码,也不知道VM是否允许intern它,或者编译器是否必须这样做。 - TofuBeer
4个回答

32

在这种特殊情况下没有区别;然而,在一般情况下它们不是相同的。

str1 += str2等同于执行以下操作:

str1 = new StringBuilder().append(str1).append(str2).toString();
为了证明这一点,只需创建一个简单的方法,它接受两个字符串并使用+=将第一个字符串连接到第二个字符串,然后检查已反汇编的字节码。
相比之下,str1.concat(str2) 只是创建一个新字符串,该字符串是str1str2的连接。对于少量连接的字符串来说,这种方法代价较小(但在连接大量字符串时将输给第一种方法)。
此外,如果str1为null,则注意str1.concat(str2)会抛出NPE,但str1 += str2则会将str1视为null而不会引发异常。(也就是说,它会返回"null"与str2的值连接起来的结果。如果str2是"foo",则最终结果是"nullfoo"。)
更新:请参见这个StackOverflow问题,该问题几乎相同。

连接字符串比较便宜。要证明这一点,请阅读JLS,而不是进行错误的推断。 - Tom Hawtin - tackline
@Tom:感谢反馈。在字符串数量较少时,concat() 方法的性能更好。但是,在处理大量字符串时,StringBuilder 更胜一筹。我已经澄清了我的回答以使其更加明确。 - John Feminella
你最后的例子是不正确的,如果str2的值为“str2”,那么相加的结果将是“nullstr2”。 连接确实会导致NPE。 - Jorn
@Jorn:啊,我的意思是“str1将被视为null”。不确定我的手指怎么打出了实际上出现的内容。感谢你的提醒。 - John Feminella

7
+=concat() 之间的重要区别不在于性能,而在于语义。 concat() 只会接受一个字符串参数,但是 + (或者 +=)会接受任何类型。如果非字符串操作数是一个对象,它将通过调用 toString() 方法转换为字符串;基本类型将被转换为相应包装类中的适当方法所示的方式,例如 Integer.toString(theInt);null 引用变为字符串 "null"
实际上,我不知道为什么 concat() 存在。人们看到它列在 API 文档中,并认为它有很好的理由 - 性能是最明显的原因。但这是一个误导;如果性能真的是关键,你应该使用 StringBuilder,就像 John 链接的线程中讨论的那样。否则,+ 或者 += 更加方便。
编辑:至于“在字符串池中创建对象”的问题,我认为您误解了字符串池的含义。在运行时,“str”和“end”的实际字符序列将存储在专用数据结构中,无论您在源代码中看到的字面量 "str""end" 在哪里,字节码都将实际包含对该数据结构中适当条目的引用。
实际上,字符串池是在加载类时填充的,而不是运行包含字符串字面量的代码时填充的。这意味着每个代码片段只创建一个对象:连接后的结果。(还有一些幕后的对象创建,对于每种技术略有不同,但性能影响不值得担忧。)

1

其中两个是通过加载代码而不是执行代码来创建的。+= 使用 StringBuilder。 - Tom Hawtin - tackline

1
我的理解是字符串池是这样的:
你似乎对这个术语有所误解。"字符串池"这个词不存在,从你的使用方式来看,看起来你只是指堆上的所有String对象。实际上,存在一个运行时常量池,其中包含编译时的String常量和由String.intern()返回的String实例,以及许多其他内容。

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