String、StringBuffer、StringBuilder 这三个类是 Java 字符串处理的核心类,本质区别:String 不可变,StringBuffer/StringBuilder 可变,底层源码围绕「字符数组存储」「不可变/可变设计」「线程安全」三大核心实现。
一、核心前置知识
- 底层存储:三者底层都是char[] 数组(JDK 9+ 优化为 byte[] + coder,减少内存占用,逻辑一致);
- 不可变 vs 可变:
- String:字符数组被
final修饰,无法修改数组内容和引用; - StringBuffer/StringBuilder:继承自
AbstractStringBuilder,字符数组无 final 修饰,支持动态扩容和修改;
- String:字符数组被
- 线程安全:StringBuffer 方法加了
synchronized锁,线程安全;StringBuilder 无锁,线程不安全。
二、String 源码解析(不可变字符串)
1. 核心成员变量
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// 核心:被 final 修饰的 char 数组 → 字符串不可变的根本原因
private final char value[];
// 缓存哈希值,避免重复计算
private int hash;
}
final class:String 类不能被继承;final char value[]:数组引用不可变,数组内容不可修改,任何字符串操作都会生成新对象。
2. 构造方法(核心)
最常用的构造器,直接复制字符数组,保证不可变:
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
- 为什么用
Arrays.copyOf?防止外部修改传入的数组,影响 String 内部值。
3. 核心方法:拼接/修改(必生成新对象)
String 没有任何修改原数组的方法,所有操作(substring/replace/+)都是新建 String 对象:
// substring 源码:新建字符数组 → 新建 String 对象
public String substring(int beginIndex) {
// 边界判断...
return new String(value, beginIndex, subLen);
}
关键结论:频繁字符串拼接(如循环中用 +),会创建大量临时对象,性能极差。
4. 不可变的优势
- 线程安全(无需同步);
- 常量池缓存,节约内存;
- 哈希值固定,适合作为 HashMap 键。
三、AbstractStringBuilder 源码解析(父类,可变核心)
StringBuffer 和 StringBuilder 都继承此类,90% 的方法(append/insert/delete)都在这个父类中实现,是可变字符串的核心。
1. 核心成员变量
abstract class AbstractStringBuilder implements Appendable, CharSequence {
// 无 final 修饰 → 数组可修改、可扩容
char[] value;
// 实际存储的字符数量(不是数组长度)
int count;
}
count:有效字符长度,value.length是数组总容量(预留扩容空间)。
2. 动态扩容机制(核心)
所有追加操作都会先判断容量,不足则自动扩容:
// 扩容核心逻辑
private void ensureCapacityInternal(int minimumCapacity) {
// 所需容量 > 当前数组长度 → 扩容
if (minimumCapacity - value.length > 0) {
// 新容量 = 原容量*2 + 2
int newCapacity = value.length * 2 + 2;
// 防止溢出,取最大值
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
// 复制数组到新容量
value = Arrays.copyOf(value, newCapacity);
}
}
- 默认初始容量:
16; - 传入字符串时:容量 = 字符串长度 + 16。
3. 核心方法:append(可变的关键)
直接在原数组追加字符,不创建新对象:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
// 扩容判断
ensureCapacityInternal(count + len);
// 直接复制到原数组,修改 count
str.getChars(0, len, value, count);
count += len;
return this;
}
关键:返回 this,支持链式调用(append().append())。
四、StringBuffer 源码解析(线程安全可变字符串)
1. 类定义
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
直接继承 AbstractStringBuilder,复用所有可变方法。
2. 核心:线程安全实现
所有对外方法都加了 synchronized 同步锁:
// append 方法:加锁
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
// 其他方法:length()/delete()/insert() 都加锁
public synchronized int length() {
return count;
}
3. 特点
- 线程安全,多线程环境下安全;
- 加锁有性能开销,速度慢于 StringBuilder。
五、StringBuilder 源码解析(非线程安全可变字符串)
1. 类定义
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
和 StringBuffer 完全一致,唯一区别:无同步锁。
2. 核心方法
直接复用父类方法,没有任何 synchronized 修饰:
public StringBuilder append(String str) {
super.append(str);
return this;
}
3. 特点
- 无线程安全保障,单线程专用;
- 无锁开销,速度最快,是单线程字符串拼接首选。
六、三者核心源码对比总结
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 底层存储 | final char[](不可变) | char[](可变,继承父类) | char[](可变,继承父类) |
| 可变性 | 不可变,操作生成新对象 | 可变,直接修改原数组 | 可变,直接修改原数组 |
| 线程安全 | 安全(不可变) | 安全(synchronized 锁) | 不安全 |
| 性能 | 最差(频繁拼接产生垃圾对象) | 中等(加锁开销) | 最快(无锁) |
| 使用场景 | 少量字符串、常量、多线程 | 多线程环境下频繁拼接 | 单线程环境下频繁拼接 |
总结
- String:不可变,底层
final char[],适合少量、不变的字符串; - AbstractStringBuilder:父类,实现可变核心和动态扩容;
- StringBuffer:线程安全,加同步锁,多线程拼接用;
- StringBuilder:非线程安全,无锁,单线程拼接首选。