频繁修改字符串应优先用StringBuilder,单线程下高效复用内存;多线程共享才考虑StringBuffer;批量拼接用String.join()或StringJoiner;编译期静态拼接可用+。

频繁修改字符串时,核心矛盾是 String 的不可变性带来的内存和性能开销。每次用 + 拼接都会生成新对象,循环中极易触发频繁 GC。解决的关键不是“怎么拼”,而是“谁来拼”——得选对可变容器。
单线程场景:无条件优先 StringBuilder
绝大多数业务代码运行在单线程上下文(如 Controller 方法内、工具类逻辑中),此时 StringBuilder 是唯一合理选择。它内部用 char[] 数组承载内容,append() 直接写入、复用内存,不产生中间 String 对象。
- 初始化时若能预估最终长度(比如拼接 100 条日志,每条平均 50 字符),建议指定容量:
new StringBuilder(5000),避免数组多次扩容拷贝 - 链式调用更简洁:
sb.append("id=").append(id).append(", name=").append(name) - 不要在循环外声明 StringBuilder 却在多个方法间传递复用——容易引发状态混乱,应在局部作用域内创建和使用
多线程共享修改:才考虑 StringBuffer
仅当同一个 StringBuilder 实例被多个线程并发调用 append() 且无法加锁隔离时,才需换成 StringBuffer。它的 append() 等方法自带 synchronized,保证线程安全,但同步块带来约 10%–15% 性能损耗。
- 常见误用:在 Spring Bean 中把 StringBuffer 当成员变量供多个请求共用——实际应每个请求新建实例,线程安全由作用域保障,而非靠 StringBuffer
- 真需跨线程协作拼接(极少见),可配合
ConcurrentHashMap或队列分片处理,而非强依赖 StringBuffer
带分隔符的批量拼接:直接用 String.join() 或 StringJoiner
当目标是把 ListString.join(",", list) 语义清晰、底层优化充分,比手写循环 + StringBuilder 更安全简洁。
立即学习“Java免费学习笔记(深入)”;
- 需要前缀/后缀(如 SQL 的
IN (?)参数占位符),用StringJoiner:new StringJoiner(",", "IN (", ")") - 避免先用
list.toString()再 replace 掉方括号和空格——既易出错又低效
编译期已知的静态拼接:放心用 +
像 "SELECT * FROM user WHERE id = " + id 这种含变量的写法,编译后仍是 StringBuilder 调用;但如果是纯字面量,如 "ERROR" + ":" + "timeout",Javac 会直接合并为常量池中的 "ERROR:timeout",零运行时开销。
- 适合配置路径、SQL 片段模板、日志固定前缀等场景
- 含 final 字段(如
final String PREFIX = "v1"; String url = PREFIX + "/api")也可能被编译器优化,但不依赖此行为做性能假设
不复杂但容易忽略


















