频繁拼接且长度未知时应使用StringBuilder,因其可复用内部数组避免GC压力;多线程需改用StringBuffer或每线程独立实例;须预估容量并及时清理,否则易出错。

字符串拼接频繁且长度不可预知时用 StringBuilder
当循环内反复追加字符串(比如日志组装、SQL 拼接、HTML 生成),String 的不可变性会导致大量临时对象,GC 压力陡增;StringBuilder 在堆上复用内部 char[] 数组,避免重复分配。
- 典型场景:遍历集合构建逗号分隔字符串、逐行读取文件后合并内容
- 不适用场景:只拼接 2–3 次固定字符串(JVM 会自动优化为
StringBuilder,手写反而冗余) - 注意初始容量:若能预估最终长度,构造时传入合理值(如
new StringBuilder(1024)),避免数组多次扩容(每次约 1.5 倍增长,涉及复制)
多线程环境必须换用 StringBuffer 或改用其他方案
StringBuilder 是非线程安全的——它的方法没加 synchronized,多线程并发调用 append() 可能导致内容错乱或 ArrayIndexOutOfBoundsException。
- 若必须在多线程中拼接字符串,直接换成
StringBuffer(语义一致,仅多了同步开销) - 更推荐做法:每个线程用独立的
StringBuilder实例,最后用toString()得到结果再合并 - 不要试图手动加锁包装
StringBuilder——容易漏锁、死锁,且性能不如StringBuffer
避免在循环外反复复用同一个 StringBuilder 实例却忘记清空
很多人为了“省对象”在方法外声明一个 StringBuilder,循环中反复 append(),但忽略调用 setLength(0) 或 delete(0, length()),导致结果不断累加。
- 错误写法:
sb.append("item: ").append(i)在下次循环前没清理 → 输出变成 "item: 1item: 2item: 3..." - 正确清理方式优先用
sb.setLength(0)(比delete()略快,不创建新对象) - 如果逻辑复杂、复用路径不清晰,不如每次新建:现代 JVM 对短生命周期对象分配/回收极快,过度复用反而增加维护成本
与 String.join()、MessageFormat、模板引擎的取舍
不是所有拼接都该用 StringBuilder。JDK 8+ 提供了更高层抽象,可读性和安全性更好。
立即学习“Java免费学习笔记(深入)”;
- 拼接集合元素:优先用
String.join(", ", list),简洁且已做性能优化 - 带格式的字符串(如 "User {0} logged in at {1}"):用
MessageFormat.format()或String.formatted()(JDK 15+),避免手动处理引号/转义 - 大段结构化内容(如 HTML、JSON):考虑
JsonGenerator或Thymeleaf等专用工具,比硬拼StringBuilder更可靠
真正需要 StringBuilder 的,是那些动态逻辑复杂、无法被高层 API 覆盖、且对吞吐量敏感的拼接路径——这时候,别省那几行代码,把容量预估和清理逻辑写清楚。











