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