在Java中,String、StringBuffer和StringBuilder是处理字符串的核心类,它们的区别主要体现在可变性、线程安全性、性能和底层实现上。以下是详细对比:
1. String(不可变字符串)
- 特点:
- 不可变性:一旦创建,内容不可修改(任何修改操作都会生成新对象)。
- 线程安全:因不可变,天然线程安全。
- 底层原理:
- Java 8及之前:使用
final char[]存储字符数据。 - Java 9+:改用
byte[]+编码标记(节省内存,Latin-1字符用1字节,UTF-16用2字节)。 - 每次修改(如拼接、替换)都会创建新对象,频繁操作可能产生大量垃圾对象。
- Java 8及之前:使用
- 应用场景:
- 字符串常量(如配置信息)。
- 少量字符串操作(编译器可能优化拼接,如
"a" + "b"编译为"ab")。 - 不适用:循环中频繁修改字符串(性能差)。
2. StringBuffer(可变字符串,线程安全)
- 特点:
- 可变性:直接修改原对象内容,不生成新对象。
- 线程安全:所有方法用
synchronized修饰,保证多线程安全。 - 性能:因同步锁,性能略低于
StringBuilder。
- 底层原理:
- 继承自
AbstractStringBuilder,内部使用char[](Java 8)或byte[](Java 9+,但行为类似字符数组)。 - 动态扩容:初始容量16字符,容量不足时按
新容量 = 旧容量*2 + 2扩容,拷贝原数据到新数组。
- 继承自
- 应用场景:
- 多线程环境下频繁修改字符串(如并发日志处理)。
3. StringBuilder(可变字符串,非线程安全)
- 特点:
- 可变性:直接修改原对象内容。
- 非线程安全:无同步锁,性能更高。
- 性能:单线程下操作速度最快(比
StringBuffer快约 10%~15%)。
- 底层原理:
- 同
StringBuffer,继承AbstractStringBuilder,使用相同动态数组结构(但无同步锁)。 - 扩容逻辑与
StringBuffer一致。
- 同
- 应用场景:
- 单线程环境下频繁修改字符串(如循环拼接SQL、JSON)。
三者的核心区别总结
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | ❌ 不可变 | ✅ 可变 | ✅ 可变 |
| 线程安全 | ✅(天然安全) | ✅(synchronized 方法) | ❌ |
| 性能 | 低(频繁修改时) | 中(同步开销) | 高(无同步开销) |
| 适用场景 | 常量、少量操作 | 多线程频繁修改 | 单线程频繁修改 |
| JDK版本 | 1.0+ | 1.0+ | 1.5+(引入优化) |
代码示例对比
// 1. String(生成1000个临时对象)
String s = "";
for (int i = 0; i < 1000; i++) {
s += i; // 每次循环创建新String对象
}
// 2. StringBuilder(单线程高效,仅1个对象)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 直接修改原对象
}
// 3. StringBuffer(多线程安全)
StringBuffer sbf = new StringBuffer();
Runnable task = () -> sbf.append("a");
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start(); t2.start(); // 线程安全追加
最佳实践
- 少量操作:直接用
String(可读性高)。 - 单线程频繁修改:用
StringBuilder(如循环拼接)。 - 多线程频繁修改:用
StringBuffer(如全局日志拼接)。 - 预分配容量:若提前知道大小,用
new StringBuilder(初始容量)避免频繁扩容。
💡 关键结论:
- 性能:
StringBuilder>StringBuffer>String(频繁修改时)。- 安全:多线程选
StringBuffer,单线程选StringBuilder。- 内存:避免在循环中用
String拼接,防止内存浪费!

被折叠的 条评论
为什么被折叠?



