String和StringBuilder以及StringBuffer的区别?
String和StringBuilder和StringBuffer的区别?
这是一个非常经典的Java面试题,也是日常开发中需要特别注意的性能要点。简单来说,三者的核心区别在于可变性、线程安全性和性能。
以下是详细的对比分析:
1. 核心区别一览表
| 特性 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 可变性 | 不可变 (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. 如何选择?
- 如果不怎么变,或者只是赋值一次:用
String- 例如:配置信息、常量、方法返回值。
- 如果是单线程,且需要频繁
append(最常见场景):用StringBuilder- 例如:
Service层拼接SQL、生成JSON/XML、日志字符串组装。
- 例如:
- 如果是多线程,且确实有多个线程同时操作同一个字符串缓冲区(非常少见):用
StringBuffer- 例如:全局缓存、共享的任务描述字符串。
总结一句话
String 是不可变的,修改会创建新对象; StringBuilder 与 StringBuffer 都是可变的,修改直接操作原对象。 其中 StringBuilder 速度最快但不安全,StringBuffer 为了线程安全付出了性能代价。 日常开发中,单线程环境请优先使用
StringBuilder。

