每个人都知道Java的String对象是不可变的,这实际上意味着如果你接受String对象a
并将它与另一个String对象连接起来,比如b
,就会创建一个全新的String对象而不是inplace concatenation。
但是,最近我在SO上阅读了this question,它告诉编译器将+
实例替换为连接运算符StringBuilder
。
此时,我有点困惑,因为据我所知并认为,StringBuilder
对象由于其内部结构而是可变的。在这种情况下,这本质上不会在Java mutable 中生成String对象吗?
答案 0 :(得分:3)
不是真的。
实际的字符串仍然是不可变的,但在编译时,JVM可以检测到某些情况,其中可以用StringBuilder替换其他String对象的创建。
因此,如果声明一个String a
并与另一个String连接,那么a
对象不会改变(因为它是不可变的),但是JVM通过用实例化替换连接来优化它一个StringBuilder,将两个字符串附加到构建器,最后分配生成的字符串。
假设你有:
String a = "banana";
String d = a + "123" + "xpto";
在JVM对此进行优化之前,您基本上会为这么简单的事情创建相对大量的字符串,即:
通过优化将串联转换为StringBuilder,JVM不再需要创建串联的中间结果,因此只需要单独的字符串和结果字符串。
这基本上是出于性能原因而做的,但请记住,在某些情况下,如果你不小心,你会为此付出巨大的代价。例如:
String a = "";
for(String str: listOfStrings){
a += str;
}
如果你正在做这样的事情,那么在每次迭代中,JVM都将实例化一个 new StringBuilder,如果listOfStrings
有很多元素,这将是非常昂贵的。在这种情况下,您应该显式使用StringBuilder
并在循环中附加而不是连接。
答案 1 :(得分:2)
字符串是不可变对象。一旦将它与另一个String连接起来,它就会成为一个新对象。所以请记住 - 您不会更改现有的String,只需创建一个新的。
答案 2 :(得分:1)
字符串确实是不可变的。编译器可能会生成涉及StringBuilder
的代码,但这只是一种不会改变代码行为方式的优化(除性能外)。如果在某些情况下您可以观察到变异(例如,通过保留对其中一个中间结果的引用),编译器将必须以仍然为该中间引用提供不可变字符串的方式进行优化。
因此,如果StringBuilder
被使用,即使您无法直接看到差异,这是否仍然意味着涉及可变性?好吧,是的,但如果你了解它,你的电脑中的所有RAM都是可变的。不可变对象的内存可以被垃圾收集器移动,这肯定也涉及到突变。最后,对于程序员来说,重要的是这个突变对你来说是隐藏的,并且你得到了一个很大的承诺,即你的程序将按照你期望的方式运行,即你永远不会在不可变对象中看到变异(除了在严重问题的情况下,例如RAM错误。)