String和StringBuilder以及StringBuffer的区别?

String和StringBuilder和StringBuffer的区别?

这是一个非常经典的Java面试题,也是日常开发中需要特别注意的性能要点。简单来说,三者的核心区别在于可变性、线程安全性和性能。

以下是详细的对比分析:

1. 核心区别一览表

特性StringStringBuilderStringBuffer
可变性不可变 (Immutable)可变 (Mutable)可变 (Mutable)
线程安全绝对安全 (底层无写操作)不安全 (非同步)安全 (方法用 synchronized 修饰)
性能最差 (修改时频繁生成新对象)最高 (直接修改内存)中等 (比String好,比StringBuilder略差)
使用场景字符串常量、少量拼接、作为Key单线程下大量字符串拼接/修改多线程下需要频繁修改字符串

2. 详细解析

2.1、String(不可变字符串)

  • 原理:String 类内部使用 final char[](JDK 8及以前)或 final byte[](JDK 9+)存储数据。一旦创建,值无法改变。

  • 修改行为:对 String 的拼接(+)、替换(replace)等操作,底层会创建新的 String 对象。旧的字符串仍存在于内存中(直到GC回收)。

  • 例子:

    String s = "Hello";
    s = s + " World";	// 这里并没有修改"Hello",而是创建了一个新的对象"Hello World",s指向了它
    
  • 优点:线程安全、不可变特性适合作为 HashMap 的Key、缓存哈希码更高效。

2.2、StringBuilder(可变字符串,非线程安全)

  • 原理:内部维护一个可变的 char[](或 byte[])数组。

  • 修改行为:append()、insert() 等方法直接修改底层数组的内容,不会创建新对象。当容量不够时,会触发自动扩容(创建一个更大的新数组,复制旧数据)。

  • 例子:

    StringBuilder sb = new StringBuilder("Hello");
    sb.append(" World"); // 直接在底层的char[]上操作,sb对象本身不变
    
  • 特点:效率最高,但不能在多线程环境下使用(没有同步保护)。

2.3、StringBuffer(可变字符串,线程安全)

  • 原理:与 StringBuilder 几乎完全一样,内部同样是可变数组。
  • 修改行为:所有涉及修改的方法(如 append、insert、delete)都加了 synchronized 关键字。
  • 代价:由于同步锁的开销,性能比 StringBuilder 慢约20%-30%。
  • 特点:适合多线程并发操作,但现实中字符串操作很少出现多线程竞争,除非是作为类变量(如Servlet中的全局缓冲区)。

3. 性能对比(直观感受)

以下操作中,String 的性能是最差的,因为它会产生大量临时对象:

// 极端低效:循环中拼接String
String str = "";
for (int i = 0; i < 10000; i++) {
    str = str + i; // 每次循环都创建新对象,O(n^2)复杂度
}

// 高效:使用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i); // 直接修改数组,O(n)复杂度
}
  • 结果:循环1万次,前者耗时可能是后者的 数百倍。

4. 特殊注意:编译期优化(JVM)

对于字面量直接拼接,Java编译器会帮我们优化:

// 这种代码编译器会直接优化为 "Hello World"
String s = "Hello" + " " + "World";
// 实际上等效于:String s = "Hello World";
  • 结论:常量拼接无需担心性能。需要担心的是变量拼接(如 String s = a + b; 或循环中拼接)。

5. 如何选择?

  1. 如果不怎么变,或者只是赋值一次:用String
    • 例如:配置信息、常量、方法返回值。
  2. 如果是单线程,且需要频繁append(最常见场景):用StringBuilder
    • 例如:Service 层拼接SQL、生成JSON/XML、日志字符串组装。
  3. 如果是多线程,且确实有多个线程同时操作同一个字符串缓冲区(非常少见):用StringBuffer
    • 例如:全局缓存、共享的任务描述字符串。

总结一句话

String 是不可变的,修改会创建新对象; StringBuilder 与 StringBuffer 都是可变的,修改直接操作原对象。 其中 StringBuilder 速度最快但不安全,StringBuffer 为了线程安全付出了性能代价。 日常开发中,单线程环境请优先使用 StringBuilder。


扫码关注

最后更新时间: 9/19/2026, 5:28:30 AM