目录

一、核心定义与设计思想

1. 核心定义

2. 设计思想

3. 核心区别速览

二、底层实现原理(含 JDK 源码分析 / 反编译验证)

1. 核心源码解析(JDK1.8)

(1)String 不可变底层

(2)AbstractStringBuilder(可变父类,核心)

(3)StringBuilder(无锁,高性能)

(4)StringBuffer(加锁,线程安全)

2. 反编译验证(字符串拼接底层)

三、代码示例

1. 基础用法全覆盖示例

四、高频踩坑点与避坑方案

坑点 1:循环中使用 + 拼接字符串(性能灾难)

坑点 2:单线程场景使用 StringBuffer

坑点 3:不指定初始容量,频繁扩容

坑点 4:String 操作后不接收返回值

坑点 5:误以为 StringBuffer 能解决所有线程问题

坑点 6:混淆三者的适用场景

五、面试高频考点与标准答案

1. String、StringBuilder、StringBuffer 的核心区别?

2. 为什么 String 不可变,另外两个可变?

3. 字符串拼接应该用哪个?循环拼接为什么不能用 +?

4. StringBuffer 线程安全的原理?

5. 三者的性能排序?

6. 什么是 StringBuilder 的扩容机制?

六、项目改造 / 落地记录

适用场景:SpringBoot 后端、字符串工具类、日志拼接、接口参数组装

1. 改造前(错误用法,企业开发严禁)

2. 改造后(企业标准用法)

3. 改造落地好处

总结


一、核心定义与设计思想

1. 核心定义

类名可变性线程安全父类核心定位
String不可变安全Object只读字符串、常量复用
StringBuilder可变不安全AbstractStringBuilder单线程字符串高效拼接
StringBuffer可变安全AbstractStringBuilder多线程字符串线程安全拼接

2. 设计思想

  1. Stringfinal 修饰的不可变类,底层存储不可修改,所有修改操作创建新对象;设计目标是安全、复用、线程安全,依托字符串常量池实现内存优化。
  2. StringBuilder / StringBuffer继承自抽象父类 AbstractStringBuilder,底层存储可动态扩容修改,无新对象创建;
    • StringBuilder:无锁设计,追求极致性能,为单线程场景设计;
    • StringBuffer:方法加 synchronized 锁,追求线程安全,为多线程场景设计。

3. 核心区别速览

特性StringStringBuilderStringBuffer
可变性不可变(final 修饰)可变可变
线程安全天然安全不安全安全(加同步锁)
性能最低(创建新对象)最高(无锁)中等(锁开销)
底层存储private final char[]char [](可扩容)char [](可扩容)
JDK 版本1.01.51.0

二、底层实现原理(含 JDK 源码分析 / 反编译验证)

1. 核心源码解析(JDK1.8)

(1)String 不可变底层
public final class String {
    // 私有+final:引用不可变、外部无法修改,保证不可变性
    private final char value[];
    // 所有修改方法(substring/replace)均返回 new String(),不修改原对象
}
(2)AbstractStringBuilder(可变父类,核心)

StringBuilder 和 StringBuffer 共用的父类,实现可变字符串的核心逻辑

abstract class AbstractStringBuilder {
    // 无final、无private修饰:数组可扩容、元素可修改
    char[] value;
    // 字符串有效长度
    int count;

    // 默认容量16
    AbstractStringBuilder() {
        value = new char[16];
    }

    // 扩容机制:新容量 = 原容量*2 + 2(核心)
    private void ensureCapacityInternal(int minimumCapacity) {
        int newCapacity = (value.length << 1) + 2;
        value = Arrays.copyOf(value, newCapacity);
    }
}
(3)StringBuilder(无锁,高性能)
public final class StringBuilder extends AbstractStringBuilder {
    // 所有方法无synchronized,无锁开销
    @Override
    public StringBuilder append(Object obj) {
        super.append(obj);
        return this;
    }
}
(4)StringBuffer(加锁,线程安全)
public final class StringBuffer extends AbstractStringBuilder {
    // 所有方法加synchronized同步锁
    @Override
    public synchronized StringBuffer append(Object obj) {
        super.append(obj);
        return this;
    }
}

2. 反编译验证(字符串拼接底层)

测试代码:

public class Test {
    public static void main(String[] args) {
        String s = "a" + "b" + "c";
    }
}

反编译命令:javap -c Test

核心结果:JDK1.8 编译器会自动将 + 拼接优化为 StringBuilder.append(),证明单线程拼接优先用 StringBuilder。


三、代码示例

1. 基础用法全覆盖示例

public class StringCompareDemo {
    public static void main(String[] args) {
        // ===================== 1. String:不可变,修改返回新对象 =====================
        String s = "hello";
        s.concat(" world"); // 无接收返回值,原字符串不变
        System.out.println(s); // hello

        // ===================== 2. StringBuilder:单线程高效拼接 =====================
        StringBuilder sb = new StringBuilder();
        sb.append("hello").append(" ").append("world"); // 链式调用
        String res1 = sb.toString();
        System.out.println(res1); // hello world

        // ===================== 3. StringBuffer:多线程安全拼接 =====================
        StringBuffer sbf = new StringBuffer();
        sbf.append("java");
        String res2 = sbf.toString();

        // ===================== 4. 性能对比:循环10000次拼接 =====================
        long start = System.currentTimeMillis();
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < 10000; i++) builder.append(i);
        System.out.println("StringBuilder耗时:" + (System.currentTimeMillis() - start) + "ms");
    }
}

四、高频踩坑点与避坑方案

坑点 1:循环中使用 + 拼接字符串(性能灾难)

  • 问题:每次循环都会创建新的 StringBuilderString 对象,触发频繁 GC;
  • 避坑方案:循环拼接必须用 StringBuilder

坑点 2:单线程场景使用 StringBuffer

  • 问题:synchronized 锁带来无意义的性能开销,性能比 StringBuilder 低 10%~30%;
  • 避坑方案:99% 的业务场景用 StringBuilder,仅明确多线程共享变量时用 StringBuffer。

坑点 3:不指定初始容量,频繁扩容

  • 问题:默认容量 16,满了就扩容(复制数组),大量拼接降低性能;
  • 避坑方案:预估字符串长度,初始化时指定容量 new StringBuilder(100)

坑点 4:String 操作后不接收返回值

  • 问题:s.replace("a","b") 原字符串不变,误以为修改成功;
  • 避坑方案:String 操作必须接收返回值 s = s.replace(...)

坑点 5:误以为 StringBuffer 能解决所有线程问题

  • 问题:StringBuffer 只保证方法原子性,不保证引用安全;
  • 避坑方案:多线程共享字符串时,额外加锁或使用并发工具类。

坑点 6:混淆三者的适用场景

  • 避坑方案:固定字符串→String;单线程拼接→StringBuilder;多线程拼接→StringBuffer。

五、面试高频考点与标准答案

1. String、StringBuilder、StringBuffer 的核心区别?

标准答案

  1. 可变性:String 不可变,后两者可变;
  2. 线程安全:String 天然安全、StringBuffer 安全(加锁)、StringBuilder 不安全;
  3. 性能:StringBuilder > StringBuffer > String;
  4. 底层:String 用 final char [],后两者继承 AbstractStringBuilder 用可变 char []。

2. 为什么 String 不可变,另外两个可变?

标准答案:String 用private final char[]存储,无修改原数组的方法;StringBuilder/StringBuffer 继承父类用无 final 的 char [],支持动态扩容和修改。

3. 字符串拼接应该用哪个?循环拼接为什么不能用 +?

标准答案

  • 少量常量拼接:直接用+(编译器优化为 StringBuilder);
  • 大量 / 循环拼接:必须用StringBuilder
  • 循环+会创建大量对象,导致 GC 频繁,性能极差。

4. StringBuffer 线程安全的原理?

标准答案:所有方法都加了synchronized同步锁,保证多线程下操作原子性。

5. 三者的性能排序?

标准答案:StringBuilder(最高,无锁)> StringBuffer(中等,锁开销)> String(最低,创建新对象)。

6. 什么是 StringBuilder 的扩容机制?

标准答案:默认容量 16,超出后自动扩容为原容量 * 2 + 2,通过Arrays.copyOf复制数组。


六、项目改造 / 落地记录

适用场景:SpringBoot 后端、字符串工具类、日志拼接、接口参数组装

1. 改造前(错误用法,企业开发严禁)

// 1. 循环用+拼接,性能极差
String result = "";
for (String str : list) {
    result += str;
}

// 2. 单线程用StringBuffer,浪费性能
StringBuffer sbf = new StringBuffer();

// 3. 无初始容量,频繁扩容
StringBuilder sb = new StringBuilder();

2. 改造后(企业标准用法)

// 1. 循环拼接:指定初始容量 + StringBuilder(单线程首选)
StringBuilder sb = new StringBuilder(list.size() * 10); // 预估容量
for (String str : list) {
    sb.append(str);
}
String result = sb.toString();

// 2. 固定字符串:直接用String
String CONSTANT = "SUCCESS";

// 3. 多线程共享拼接:用StringBuffer
StringBuffer sbf = new StringBuffer();

3. 改造落地好处

  1. 性能提升:循环拼接效率提升 10 倍以上,减少 GC;
  2. 资源优化:指定容量避免频繁扩容,节约内存;
  3. 规范统一:符合阿里 Java 开发手册,团队协作无歧义;
  4. 线程安全:多线程场景避免数据错乱。

总结

  1. 核心选型:固定字符串用 String,单线程拼接用 StringBuilder,多线程拼接用 StringBuffer;
  2. 底层关键:String 不可变(final char []),后两者可变(继承 AbstractStringBuilder);
  3. 性能铁律:循环拼接禁止用+,必须用 StringBuilder;
  4. 实战规范:99% 的业务场景用 StringBuilder,初始化尽量指定容量。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐