深入理解 StringBuffer、+ 运算符、formatted() 的底层原理与性能对比
Java 8 及之前:
编译器会将 +转换成 StringBuilder的 append(),然后调用 toString()。
// 例如:
"a" + "b" + "c"
// → 编译后等价于:
new StringBuilder().append("a").append("b").append("c").toString()
在循环中使用 +(如 for内 s += i):
每次循环都会新建一个 StringBuilder,产生大量临时对象,性能极差。
Java 9+:
引入了 invokedynamic+ StringConcatFactory,不再机械生成 StringBuilder,而是动态生成更高效的拼接指令(直接操作字符数组)。单行 +的性能已非常接近手写的 StringBuilder。
append、insert等)都用 synchronized修饰。char[]/ byte[],支持动态扩容。StringBuffer比 StringBuilder慢。StringBuffer做拼接(字符串拼接通常是线程局部的)。StringBuffer不是最快的,而是最慢的可变字符串拼接方式(相对于 StringBuilder)。StringBuilder,它是当时唯一的高效可变字符串类。java.util.Formatter,会解析格式说明符(如 %s、%d)。Formatter对象,可能涉及正则、类型转换、Locale 处理。StringBuilder或 char[])。| 方式 | 性能排名 | 说明 |
|---|---|---|
StringBuilder |
最快 | 无锁,可变,可复用 |
+(单行/常量折叠) |
很快 | Java 9+ 已高度优化,接近 StringBuilder |
+(循环内拼接) |
很慢 | 每次循环新建 StringBuilder |
StringBuffer |
较慢 | 同步开销,不推荐单线程使用 |
formatted()/ String.format() |
最慢 | 功能强大但开销大 |
| 场景 | 推荐写法 |
|---|---|
| 单行简单拼接(已知固定字符串) | 直接用 +,代码最清晰 |
| 循环内拼接 / 动态大量拼接 | 手动创建 StringBuilder并复用 |
| 多线程环境 | 依然使用局部 StringBuilder,最终得到不可变 String再共享;无需 StringBuffer |
| 需要格式化输出(数字、日期、对齐) | 使用 String.format或 formatted(),避免在高频循环中调用 |
// 错误:循环内用 + 拼接
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 每次循环新建 StringBuilder
}
// 正确:循环内复用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String s = sb.toString();
StringBuilder> +(单行)> +(循环)≈ StringBuffer> formatted()StringBuilder,格式化需求用 format系列方法。StringBuffer,它在现代 Java 中已经不是性能选项。