0

每个人都知道 Java 的 String 对象是不可变的,这实质上意味着如果您将 String 对象a与另一个 String 对象连接起来,例如b,将创建一个全新的 String 对象,而不是就地连接。

但是,最近我在 SO 上阅读了这个问题,它告诉编译器将连接运算符+替换为StringBuilder实例。

在这一点上,我有点困惑,因为据我所知和认为,StringBuilder对象由于它们的内部结构是可变的。在这种情况下,这不会本质上使 Java 中的 String 对象可变吗?

4

3 回答 3

3

并不真地。

实际的 Strings 仍然是不可变的,但在编译时,JVM 可以检测到一些情况,其中额外的 String 对象的创建可以被 StringBuilder 替换。

因此,如果您声明一个字符串a并与另一个字符串连接,您的a对象不会更改(因为它是不可变的),但 JVM 通过将连接替换为 StringBuilder 的实例化、将两个字符串附加到 Builder 来优化这一点,最后分配结果字符串。

假设您有:

String a = "banana";
String d = a + "123" + "xpto";

在 JVM 对此进行优化之前,您实际上会为如此简单的事情创建相对大量的字符串,即:

  • 字符串
  • 字符串“123”
  • 字符串“xpto”
  • 字符串 a + "123"
  • 字符串 a+"123"+"xpto"

通过将 concatenation 转换为 StringBuilder 的优化,JVM 不再需要创建 concatenation 的中间结果,因此只需要单个 Strings 和结果一个。

这基本上是出于性能原因,但请记住,在某些情况下,如果您不小心,您将为此付出巨大的代价。例如:

String a = "";
for(String str: listOfStrings){
    a += str;
}

如果你在做这样的事情,在每次迭代中,JVM 都会实例化一个listOfStrings的 StringBuilder,如果有很多元素,这将是非常昂贵的。在这种情况下,您应该StringBuilder显式使用 a 并在循环内进行追加而不是连接。

于 2013-04-14T18:11:02.020 回答
2

字符串是不可变的对象。一旦你将它与另一个字符串连接起来,它就会变成一个新对象。所以请记住 - 您不会更改现有字符串,您只需创建一个新字符串。

于 2013-04-14T18:14:45.860 回答
1

字符串确实是不可变的。编译器可能会生成涉及 的代码StringBuilder,但这只是一种优化,不会改变代码的行为方式(除了性能)。如果在某些情况下您可以观察到突变(例如,通过保留对中间结果之一的引用),编译器必须以一种仍然为您提供该中间引用的不可变字符串的方式进行优化。

所以如果StringBuilder在幕后使用,即使你不能直接看到差异,那不仍然意味着涉及可变性吗?嗯,是的,但如果你认真对待,你 PC 中的所有 RAM 都是可变的。不可变对象的内存可以被垃圾收集器移动,这肯定也涉及到变异。最后,作为程序员,对你来说最重要的是这个突变对你来说是隐藏的,你得到一个很大的承诺,你的程序会按照你期望的方式运行,即你永远不会在不可变对象中看到突变(除了如果出现严重问题,例如 RAM 故障)。

于 2013-04-14T18:23:44.680 回答