关于字符串连接行为

About string concatenation behaviours

我明白,鉴于字符串的不变性,

String a="";
for(int i=0;i++<9;)
    a+=i;

效率非常低,因为最初一个字符串被实例化并放入 字符串池 ,然后 a+=i 创建一个新字符串( 0 在第一个循环中),被 a 引用并且前一个现在有资格进行垃圾收集。这种情况发生了九次。

更好的方法是使用 StringBuilder:

StringBuilder a=new StringBuilder("");
for(int i=0;i++<9;)
    a.append(i);

但是当我用 new关键字?

String a=new String("");
for(int i=0;i++<9;)
    a+=i;

我知道在这种情况下 a 不会被驻留(它不在字符串池中),但它仍然是不可变的吗? a+=i指令在这个时候做了什么?该行为是否与我的第一个示例相同?

只有 String 文字 或调用 intern() 方法的 Strings 被放入字符串池中。串联不会自动实习 String,因此您的示例在字符串池方面将是相同的。

String abc = new String("abc"); //"abc" is put on the pool
abc += "def"; //"def" is put on the pool, but "abcdef" is not
String xyz = "abcdefghi".substring(0, 6).intern(); //"abcdef" is now added to the pool and returned by the intern() function
String xyz = "test"; //test is put on the pool
xyz += "ing"; //ing is put on the pool, but "testing" is not

并对此进行扩展,请注意 String 构造函数不会自动实习(或 实习)字符串。使用 String literals(代码中引号中的字符串)是导致 String 在 String 池中的原因。

String abc = "abc"; //"abc" is in the pool
String def = "def"; //"def" is in the pool
String str1 = new String(abc + def); //"abcdef" is not in the pool yet
String str2 = new String("abcdef"); //"abcdef" is on the pool now

另请注意,String 复制构造函数几乎从未使用过,因为无论如何字符串都是不可变的。

有关详细信息,请阅读答案 here, here, and here

让我们考虑你的第一个例子:

String a="";
for(int i=0;i++<9;)
    a+=i;

这段代码会这样执行:

String a="";
a=new StringBuilder(a).append(0).toString();
a=new StringBuilder(a).append(1).toString();
a=new StringBuilder(a).append(2).toString();
a=new StringBuilder(a).append(3).toString();
a=new StringBuilder(a).append(4).toString();
a=new StringBuilder(a).append(5).toString();
a=new StringBuilder(a).append(6).toString();
a=new StringBuilder(a).append(7).toString();
a=new StringBuilder(a).append(8).toString();
a=new StringBuilder(a).append(9).toString();

因此,对于每个循环迭代,您都会从字符串创建一个新的 StringBuilder(每次分配一个新的内部 char[] 缓冲区),它将被转换回 String每次。另一方面,在第二种情况下,您将有效地拥有

StringBuilder a=new StringBuilder("").append(0).append(1).append(2).append(3)
    .append(4).append(5).append(6).append(7).append(8).append(9);

所以你将只有一个 StringBuilder 和一个内部 char[] 缓冲区(在你的情况下它不会被重新分配,因为所有附加不超过 [=18 的初始容量=]).所以它更快,因为你有更少的对象并且没有任何东西被复制多次。

a=new String("") 是没有用的,因为除了你已经有的空字符串(它是在你的 class 常量池加载期间创建和驻留的)之外,你还会有一个空字符串。除了额外的空字符串(在第一次循环迭代和垃圾收集后将不再使用)之外,它与第一种情况没有区别。

请注意,JLS 中并未明确指定如何实现字符串连接(使用隐式 StringBuilder 或其他技术),但通常 java 编译器使用 StringBuilder 对其进行翻译。