String、StringBuilder 和 StringBuffer 区别详解
2025/11/30大约 4 分钟
📝 Java:String、StringBuilder 和 StringBuffer 区别笔记
本笔记将从 可变性、线程安全 和 性能 三个方面来阐述这三者之间的主要区别。
一、核心特性对比
| 特性 | String | StringBuilder | StringBuffer |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 是 (因为不可变) | 否 (非线程安全) | 是 |
| 性能 | 较低 | 最高 | 中等 |
| 适用场景 | 少量字符串操作 | 单线程大量操作 | 多线程大量操作 |
二、详细解释
1. 🔍 可变性 (Mutability)
| 类 | 描述 |
|---|---|
| String | 是 Java 中最基本的字符串类型,表示不可变的字符序列。一旦创建,其内容就不能被修改。 |
| StringBuilder | 是 Java 5 引入的可变字符序列,非线程安全,但性能较高。 |
| StringBuffer | 是 Java 早期版本提供的可变字符序列,线程安全,但性能相对较低。 |
String 的不可变性示例:
当对 String 进行拼接操作时,实际上是创建了新的对象,而不是修改原对象。
String str = "hello";
str = str + " world"; // 实际上是创建了新对象,而非修改原对象StringBuilder/StringBuffer 的可变性示例:
它们的方法直接修改对象本身的内容。
StringBuilder sb = new StringBuilder("hello");
sb.append(" world"); // 直接修改原对象2. 🛡️ 线程安全性 (Thread Safety)
- String: 天然线程安全,因为它是不可变的。
- StringBuilder: 没有同步措施,因此是非线程安全的。
- StringBuffer: 其方法使用了
synchronized关键字修饰,保证了线程安全。
3. ⚡ 性能比较 (Performance)
在大多数情况下,性能排序为:
$$\text{StringBuilder} > \text{StringBuffer} > \text{String}$$
String 拼接操作的性能测试示例 (低效):
在循环中大量使用 + 进行 String 拼接效率低下,因为每次循环都会创建新的 String 对象。
public class StringPerformanceTest {
private static final int COUNT = 10000; // 定义循环次数
public static void main(String[] args) {
// 1. String 拼接测试
testStringConcatenation();//100秒
System.out.println("-------------------------");
// 2. StringBuilder 拼接测试 (推荐用于单线程环境)
testStringBuilderConcatenation();//0秒
System.out.println("-------------------------");
// 3. StringBuffer 拼接测试 (推荐用于多线程环境)
testStringBufferConcatenation();//1秒
}
/**
* 使用 String 进行拼接的测试方法。
* 每次循环都会创建新的 String 对象,性能最差。
*/
public static void testStringConcatenation() {
long start = System.currentTimeMillis();
String result = ""; // String 是不可变的
for (int i = 0; i < COUNT; i++) {
result += i; // 每次执行相当于 result = new String(result + i)
}
long end = System.currentTimeMillis();
System.out.println("String 拼接耗时 (" + COUNT + "次): " + (end - start) + " ms");
}
/**
* 使用 StringBuilder 进行拼接的测试方法。
* 直接修改内部数组,非线程安全,性能最好。
*/
public static void testStringBuilderConcatenation() {
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder(); // StringBuilder 是可变的
for (int i = 0; i < COUNT; i++) {
sb.append(i); // 直接修改 sb 内部内容
}
String result = sb.toString(); // 最终转换为 String
long end = System.currentTimeMillis();
System.out.println("StringBuilder 拼接耗时 (" + COUNT + "次): " + (end - start) + " ms");
}
/**
* 使用 StringBuffer 进行拼接的测试方法。
* 直接修改内部数组,线程安全(方法带 synchronized),性能中等。
*/
public static void testStringBufferConcatenation() {
long start = System.currentTimeMillis();
StringBuffer sb = new StringBuffer(); // StringBuffer 是可变的,且线程安全
for (int i = 0; i < COUNT; i++) {
sb.append(i); // 直接修改 sb 内部内容 (同步方法)
}
String result = sb.toString();
long end = System.currentTimeMillis();
System.out.println("StringBuffer 拼接耗时 (" + COUNT + "次): " + (end - start) + " ms");
}
}三、💡 使用小技巧 (Usage Tips)
如何选择 StringBuilder 和 StringBuffer?
当程序中有大量字符串拼接的需求时,应该使用
StringBuilder或StringBuffer。- 如果需要考虑线程安全问题(多线程环境),应使用
StringBuffer。 - 如果做字符串拼接不需要考虑线程安全(单线程环境),那么可以使用
StringBuilder。StringBuilder相比StringBuffer移除了synchronized关键字,因此效率更高。
- 如果需要考虑线程安全问题(多线程环境),应使用
关于容量 (Capacity) 设置
如果我们有大量的字符串拼接任务,并且能够预先判断出最终字符串的大致大小,最好在
new StringBuilder()或new StringBuffer()时设置好capacity。- 这样可以避免后续扩容增加开销(扩容需要抛弃原数组,然后拷贝数组创建新数组)。
如果您想深入了解 Java 内存模型或并发编程,我可以为您搜索相关资料。
