🎯 一句话总结
类名 | 是否可变 | 线程安全 | 性能 | 适用场景 |
---|
String | ❌ 不可变 | ✅ 安全(不可变特性) | 最慢 | 少量字符串拼接、常量使用场景 |
StringBuilder | ✅ 可变 | ❌ 不安全 | 最快 | 单线程场景下大量字符串拼接 |
StringBuffer | ✅ 可变 | ✅ 安全 | 中等 | 多线程下的字符串操作 |

📦 一、核心区别详解
1. String
:不可变字符串
String s = "hello";
s = s + "world";
- 所有对
String
的修改,都会 产生新的对象(因为底层是 final char[])。 - 线程安全:因为不可变,对并发访问天然安全。
- 常用场景:
- 字符串常量(如配置 key、路径名)
- 少量拼接(JVM 会用 String Pool 优化)
- 做为 map 的 key(因 hashCode 不变)
2. StringBuilder
:可变字符串(推荐用于单线程拼接)
StringBuilder sb = new StringBuilder();
sb.append("hello").append("world");
- 底层维护一个可扩容的
char[]
数组,append 时直接在原数组上操作。 - 线程不安全,但性能最优
- 常用场景:
- 单线程内的大量字符串拼接(如日志、SQL 拼接、JSON 拼接等)
- 性能敏感的字符串操作
3. StringBuffer
:线程安全的可变字符串
StringBuffer sb = new StringBuffer();
sb.append("hello").append("world");
- 和
StringBuilder
一样底层是可变数组,但方法全部加了 synchronized
。 - 适用于并发环境,但性能低于
StringBuilder
- 常用场景:
🔬 二、源码层面对比
特性 | String | StringBuilder | StringBuffer |
---|
是否 final | ✅ 是 | ❌ 否 | ❌ 否 |
线程安全 | ✅ 安全 | ❌ 不安全 | ✅ 安全(方法加锁) |
性能 | ❌ 最低 | ✅ 最好 | ⚠️ 中等 |
底层结构 | final char[] | char[] | char[] |
是否可变 | ❌ 不可变 | ✅ 可变 | ✅ 可变 |
🚀 三、真实业务场景举例
✅ 适合用 String
的场景:
- 用户请求路径:
String path = "/api/user/info";
- 配置文件 key:
String redisKey = "user:info:123";
- Map 的 Key 值(不变性保证 hashCode 一致)
✅ 适合用 StringBuilder
的场景:
StringBuilder sql = new StringBuilder();
sql.append("SELECT * FROM users");
if (condition != null) {
sql.append(" WHERE name = ").append(condition);
}
✅ 适合用 StringBuffer
的场景:
public synchronized String buildHtml() {
StringBuffer sb = new StringBuffer();
sb.append("<html>").append("<body>");
return sb.toString();
}
🎯 四、使用建议总结
场景 | 推荐使用 |
---|
字符串常量、不变性要求 | ✅ String |
单线程拼接字符串 | ✅ StringBuilder |
多线程拼接字符串 | ✅ StringBuffer(或使用线程安全的构建方式) |
📌 面试陷阱题:你会选哪个?
问:如下代码哪种方式性能更优?
String s = "";
for (int i = 0; i < 10000; i++) {
s += i;
}
- ❌ 错误写法,性能极差!(每次拼接都创建新对象)
- ✅ 正确做法:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}