String、StringBuffer、StringBuilder 源码解析

3 阅读4分钟

String、StringBuffer、StringBuilder 这三个类是 Java 字符串处理的核心类,本质区别:String 不可变,StringBuffer/StringBuilder 可变,底层源码围绕「字符数组存储」「不可变/可变设计」「线程安全」三大核心实现。

一、核心前置知识

  1. 底层存储:三者底层都是char[] 数组(JDK 9+ 优化为 byte[] + coder,减少内存占用,逻辑一致);
  2. 不可变 vs 可变
    • String:字符数组被 final 修饰,无法修改数组内容和引用
    • StringBuffer/StringBuilder:继承自 AbstractStringBuilder,字符数组无 final 修饰,支持动态扩容和修改;
  3. 线程安全: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. 不可变的优势

  1. 线程安全(无需同步);
  2. 常量池缓存,节约内存;
  3. 哈希值固定,适合作为 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. 特点

  • 无线程安全保障,单线程专用;
  • 无锁开销,速度最快,是单线程字符串拼接首选。

六、三者核心源码对比总结

特性StringStringBufferStringBuilder
底层存储final char[](不可变)char[](可变,继承父类)char[](可变,继承父类)
可变性不可变,操作生成新对象可变,直接修改原数组可变,直接修改原数组
线程安全安全(不可变)安全(synchronized 锁)不安全
性能最差(频繁拼接产生垃圾对象)中等(加锁开销)最快(无锁)
使用场景少量字符串、常量、多线程多线程环境下频繁拼接单线程环境下频繁拼接

总结

  1. String:不可变,底层 final char[],适合少量、不变的字符串;
  2. AbstractStringBuilder:父类,实现可变核心和动态扩容;
  3. StringBuffer:线程安全,加同步锁,多线程拼接用;
  4. StringBuilder:非线程安全,无锁,单线程拼接首选