在 Java 中,String、StringBuffer 和 StringBuilder 都用于处理字符串,但它们在可变性、线程安全性、性能上有本质区别。下面从三个维度对比,并给出使用建议。
一、基本对比
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 安全(不可变天然安全) | 安全(方法用 synchronized 修饰) | 不安全 |
| 性能 | 拼接时会产生大量临时对象,效率低 | 同步开销,比 StringBuilder 慢 | 最快 |
| 引入版本 | JDK 1.0 | JDK 1.0 | JDK 1.5 |
二、详解
1. String:不可变字符序列
- 一旦创建,其内容无法修改。任何“修改”操作(如
+、concat、substring)都会生成新的String对象。 - 优点:线程安全、可缓存(如字符串常量池)、适合作为 HashMap 的键。
- 缺点:频繁拼接时会产生大量中间对象,造成内存浪费和 GC 压力。
示例:
java
String s = "Hello";
s += " World"; // 实际生成了新的 String 对象
2. StringBuffer:可变且线程安全
- 内部维护一个可变的字符数组,提供
append()、insert()等方法修改内容。 - 所有修改方法都加了
synchronized关键字,保证多线程环境下安全。 - 适用于多线程字符串操作场景(如日志记录、多线程拼接)。
示例:
java
StringBuffer sb = new StringBuffer();
sb.append("Hello").append(" World"); // 不会产生新对象
3. StringBuilder:可变但不安全
- 与
StringBuffer功能完全相同,只是方法没有synchronized修饰。 - 单线程环境下性能最佳(无锁竞争)。
- 是 Java 1.5 引入的,用于替代
StringBuffer在单线程场景。
示例:
java
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" World");
三、性能对比与使用建议
1. 字符串拼接的优化
错误示例:
java
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 每次创建新的String
}
正确示例:
java
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String s = sb.toString();
2. 使用场景总结
- 单线程、频繁修改:使用
StringBuilder(如循环拼接、JSON 构造)。 - 多线程、频繁修改:使用
StringBuffer(如多线程日志输出)。 - 字符串内容不变:使用
String(如常量、配置项、HashMap 的键)。 - 少量拼接:编译器会自动优化,直接用
+即可。
四、扩展知识
-
初始容量:
StringBuilder和StringBuffer默认初始容量为 16,如果预估最终字符串长度,可以在构造时指定容量,避免扩容带来的性能损耗。java
StringBuilder sb = new StringBuilder(128); // 预分配容量 -
线程安全成本:
StringBuffer因为同步开销,在单线程下比StringBuilder慢约 10%~30%(取决于操作复杂度)。
五、总结
| 场景 | 推荐类 |
|---|---|
| 字符串常量或少量拼接 | String |
| 单线程大量拼接 | StringBuilder |
| 多线程大量拼接 | StringBuffer |
理解这三者的本质区别,可以帮助你写出更高效、更安全的 Java 代码。