String、StringBuffer、StringBuilder 全面对比

72 阅读3分钟

1. 基本定义

类名所在包可变性线程安全性
Stringjava.lang不可变(immutable)线程安全(不可变带来的天然安全)
StringBufferjava.lang可变(mutable)线程安全(方法加 synchronized
StringBuilderjava.lang可变(mutable)非线程安全

2. 不可变与可变的原理

String

  • 内部是一个 final char[] value(Java 9+ 是 byte[] value + coder)。

  • 一旦创建,内容不可改变;任何拼接、替换操作都会生成新对象

  • 优点

    • 线程安全。
    • 可用于常量池(字符串常量复用)。
    • 哈希值可缓存(hashCode 只算一次)。
  • 缺点

    • 拼接频繁时性能差(不断创建临时对象)。

StringBuffer / StringBuilder

  • 内部维护一个可变的 char[],容量不足时动态扩容(newCapacity = oldCapacity*2 + 2)。

  • 调用 appendinsertdelete 不会新建对象,而是修改同一缓冲区。

  • 区别:

    • StringBuffer:方法加 synchronized,多线程安全,单线程下性能慢。
    • StringBuilder:不加锁,单线程下性能优于 StringBuffer
  • 初始容量

    • 无参构造默认 16
    • 可指定初始容量
  • 扩容触发

    • count + 追加长度 > 当前容量
  • 扩容公式

    java
    复制编辑
    newCapacity = oldCapacity * 2 + 2
    if (newCapacity < minimumCapacity) {
        newCapacity = minimumCapacity;
    }
    value = Arrays.copyOf(value, newCapacity);
    
  • 过程

    1. 分配更大数组
    2. 复制旧内容
    3. 写入新数据

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 设计成不可变?

  1. 安全性(类加载器、反射、文件路径、网络地址等常用 String 表示,不能被恶意修改)。
  2. 线程安全(可共享,适合常量池复用)。
  3. 缓存哈希值(用于 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 → 可变、无锁、适合单线程大量拼接