跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

在Java中StringBuffer和StringBuilder如何选择_Java字符串类对比解析

多线程共享场景必须用StringBuffer,因其方法均synchronized保证单次调用原子性;单线程优先选StringBuilder以提升10%–20%性能;构造时应预估容量避免频繁扩容;toString()返回新String,修改buffer不影响已有字符串副本。 多线程环境下必须用
StringBuffer
当字符串操作发生在多个线程共享的上下文中(比如 Servlet 的成员变量、静态工具类方法),
StringBuffer
是唯一安全的选择。它的所有公开方法(如
append()
insert()
reverse()
)都加了
synchronized
,能保证操作原子性。 但要注意:同步只覆盖单个方法调用,不保证连续调用的原子性。例如:
buffer.append("a").append("b"); // 两次 synchronized 方法调用,中间可能被其他线程插入
常见误用场景: 把
StringBuffer
当作“线程安全的 StringBuilder”直接塞进高并发循环里,却忽略了锁竞争带来的性能断崖 在局部方法内(无共享对象)仍用
StringBuffer
,纯属浪费同步开销 单线程性能敏感场景一律选
StringBuilder
StringBuilder
StringBuffer
的非同步镜像,API 完全一致,但去掉所有
synchronized
。在大多数本地字符串拼接、JSON 构建、模板渲染等场景中,它比
StringBuffer
快 10%–20%,JVM 还可能对其做额外优化(如逃逸分析后栈上分配)。 立即学习 “ Java免费学习笔记(深入) ”; 典型适用情况: 方法内临时拼接:
new StringBuilder().append(name).append("@").append(domain).toString()
循环内累积日志:
for (String s : list) { builder.append(s).append("\n"); }
Spring MVC 中
@ResponseBody
返回前的 JSON 组装 注意:不要复用
StringBuilder
实例跨方法传递,除非你明确控制其生命周期和线程归属——否则容易引发状态污染或内存泄漏。 Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载 构造时指定容量能避免多次数组扩容 两者底层都用
char[]
数组存储,初始容量默认为 16。如果预估最终长度远超 16(比如拼接 500 个字符串),不设初始容量会导致频繁扩容(每次约 1.5 倍增长 + 数组拷贝),显著拖慢性能。 推荐做法: 知道大致长度时,直接传入:
new StringBuilder(1024)
new StringBuffer(2048)
拼接已知集合,用
list.size() * avgLength
估算(比如 100 个平均 20 字符的字符串 → 初始容量设为 2500) 不确定长度但量级大,宁可略高估(如设 8192),也别依赖默认值反复扩容 扩容代价在 GC 日志中常表现为大量
char[]
短生命周期对象,可通过
-XX:+PrintGCDetails
观察。 别混淆
toString()
String.valueOf()
StringBuffer.toString()
StringBuilder.toString()
都返回新
String
对象,内容是当前缓冲区快照;而
String.valueOf(buffer)
会调用其
toString()
,本质相同。但有人误以为
String.valueOf()
更“轻量”,其实没有区别。 真正要警惕的是这个反模式:
String s = buffer.toString(); // ✅ 正确:获取副本 buffer.setLength(0); // ❌ 错误:清空后 s 不受影响,但若后续继续用 buffer,得确保没人引用旧 toString 结果
更隐蔽的问题:某些框架(如旧版 Log4j)内部缓存了
toString()
结果,如果之后修改了 buffer 内容,日志可能输出意外值——这和 String 不可变的设计初衷相悖,务必隔离缓冲区生命周期。

相关文章