原文:Java 字符串拼接原理
我们知道 Java 可以直接使用加号+
来拼接字符串。
字符串+
拼接的本质是使用StringBuilder.append()
(已在Java8
测试通过),最终如果要赋值给字符串变量时,会调用toString()
。
/*** 字符串追加*/
@Test
public void testStrAppend() {/** + 之前先隐式实例化 StringBuilder 对象* 每次 + 都是 append,期间不会 toString* 最后赋值给字符串时,会隐式调用 toString() 得到结果字符串*/String abc = "2";String def = "3";String str = "1" + abc + def;System.out.println(str);
}
特殊地,如果是字符串常量拼接,编译器会在编译期自动优化到一起,例如"1" + "2" + "3"
会自动优化为"123"
。
使用+
拼接字符串是 Java 语法糖。
/*** 字符串常量拼接*/
@Test
public void testConstantAppend() {// 如果是常量相加,那么编译器在编译为 class 文件时,会自动优化为一个常量String str = "1" + "2" + "3";
}
还需要注意的是在循环中如果使用+=
其实会造成StringBuilder
重复创建,导致资源浪费,因为每次+=
都会 new 一次StringBuilder
,并隐式toString()
赋值给原字符串对象。
/*** 在循环中追加字符串* 在循环中 += 会造成资源浪费*/
@Test
public void testStrAppendInLoop() {/** 循环中不推荐使用 +=* 每次 += 相当于实例化一个新的 StringBuilder,然后调用 append(oldStr)、append(newStr)、toString()* 产生大量的 StringBuilder 对象*/String str = "";for (int i = 0; i < 100; i++) {str += i;}System.out.println(str);
}
总结:
对于字符串使用+
拼接:
-
如果是常量相加,那么在编译器会自动合并为一个常量字符串(只要挨着的常量就会合并,前后声明不影响,例如
"1" + "2" + def + "4" + "5"
会自动合并为"12" + def + "45"
) -
如果与变量相加,那么相加前会初始化
StringBuilder
对象,每次相加都append(str)
,如果赋值给String
,最终会隐式调用toString()
(即只在最后调用一次toString()
,这是我们期望的)
需要注意的是,使用+=
拼接,例如a += 123;
等效于a = a + 123;
,每次+=
都会实例化一个新的StringBuilder
对象,append(a).append(123)
再隐式调用toString()
(注意:每次+=
因为要再次赋值给字符串a
都会隐式toString()
)。
所以,如果在循环中使用+=
,会产生大量StringBuilder
对象(每次相加前都会实例化StringBuilder
对象)和String
对象(每次都会toString()
),造成资源浪费。
最佳实践:
-
普通的字符串拼接,可以使用
+
拼接字符串(与手动创建StringBuilder
对象,调用append()
等效) -
循环中字符串拼接,推荐手动创建
StringBuilder
对象调用append()
,而不是+=
(循环中+=
会创建大量StringBuilder
和String
对象)