1. 基本定义
| 类名 | 所在包 | 可变性 | 线程安全性 |
|---|---|---|---|
String | java.lang | 不可变(immutable) | 线程安全(不可变带来的天然安全) |
StringBuffer | java.lang | 可变(mutable) | 线程安全(方法加 synchronized) |
StringBuilder | java.lang | 可变(mutable) | 非线程安全 |
2. 不可变与可变的原理
String
-
内部是一个
final char[] value(Java 9+ 是byte[] value+coder)。 -
一旦创建,内容不可改变;任何拼接、替换操作都会生成新对象。
-
优点:
- 线程安全。
- 可用于常量池(字符串常量复用)。
- 哈希值可缓存(
hashCode只算一次)。
-
缺点:
- 拼接频繁时性能差(不断创建临时对象)。
StringBuffer / StringBuilder
-
内部维护一个可变的
char[],容量不足时动态扩容(newCapacity = oldCapacity*2 + 2)。 -
调用
append、insert、delete不会新建对象,而是修改同一缓冲区。 -
区别:
StringBuffer:方法加synchronized,多线程安全,单线程下性能慢。StringBuilder:不加锁,单线程下性能优于StringBuffer。
-
初始容量:
- 无参构造默认 16
- 可指定初始容量
-
扩容触发:
count + 追加长度 > 当前容量
-
扩容公式:
java 复制编辑 newCapacity = oldCapacity * 2 + 2 if (newCapacity < minimumCapacity) { newCapacity = minimumCapacity; } value = Arrays.copyOf(value, newCapacity); -
过程:
- 分配更大数组
- 复制旧内容
- 写入新数据
3. 性能对比
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 少量字符串拼接(<10 次) | String | 编译期会用 StringBuilder 优化常量拼接 |
| 大量拼接、单线程 | StringBuilder | 避免多线程同步开销 |
| 大量拼接、多线程 | StringBuffer | 保证线程安全 |
4. 代码示例
不可变的 String
java
复制编辑
String s = "hello";
s += " world";
System.out.println(s); // "hello world"
// 实际生成了新的 String 对象,原 "hello" 未变
可变的 StringBuilder
java
复制编辑
StringBuilder sb = new StringBuilder("hello");
sb.append(" world");
System.out.println(sb.toString()); // "hello world"
// sb 还是同一个对象
5. 常见面试追问
Q1: 为什么 String 设计成不可变?
- 安全性(类加载器、反射、文件路径、网络地址等常用 String 表示,不能被恶意修改)。
- 线程安全(可共享,适合常量池复用)。
- 缓存哈希值(用于 Map/Set 作为 key 时性能更高)。
Q2: 频繁拼接为什么不用 String?
- 因为每次拼接都会生成新对象,旧对象进入 GC,性能差。
Q3: 编译器会优化哪些 String 拼接?
- 常量 + 常量 会直接合并成一个常量:
String s = "a" + "b"; // 编译期直接成 "ab" String s = a + b; // 编译后相当于 new StringBuilder().append(a).append(b).toString()
Q4:StringBuilder.toString() 会复制数据吗?
- 会,新建 String 并复制内部数组内容,保证外部不可修改内部数据。
Q5:扩容为什么不是直接翻倍?
- 历史实现是 2 倍 + 2,兼顾小容量时的频繁追加场景,减少扩容次数。
Q6:如何减少扩容次数?
- 预估长度,一次性给够容量:new StringBuilder(500);
6. 总结口诀
“常量池里放 String,线程安全用 Buffer,单线程高效用 Builder。”
- String → 不可变、线程安全、适合少量拼接和常量场景
- StringBuffer → 可变、同步锁、适合多线程拼接
- StringBuilder → 可变、无锁、适合单线程大量拼接