什么是 StringBuffer?
StringBuffer 是 Java 中一个用于创建可变字符串的类,与 Java 中不可变的 String 类不同,StringBuffer 对象的内容可以被修改,比如可以在字符串的末尾追加、插入或删除字符。

你可以把它想象成一个“字符序列”的容器,你可以随时向这个容器里添加或移除字符,而这个容器本身(对象引用)保持不变。
StringBuffer 的核心特性
- 可变性:这是
StringBuffer最核心的特性,一旦创建了StringBuffer对象,你就可以修改它所包含的字符序列,而无需像String那样创建新的对象。 - 线程安全:
StringBuffer的所有公共方法(如append(),insert(),delete()等)都被synchronized关键字修饰,这意味着在任何时刻,只有一个线程可以执行这些方法。StringBuffer是线程安全的,可以在多线程环境中安全使用。 - 性能:由于线程安全需要额外的同步开销,
StringBuffer的性能通常比它的非线程安全版本StringBuilder稍慢,在单线程环境下,推荐使用StringBuilder以获得更好的性能。 - 字符序列:它内部维护一个字符数组,当容量不足以容纳新添加的字符时,会自动扩容(通常是创建一个更大的新数组,并复制旧数据)。
StringBuffer 与 String 的关键区别
| 特性 | String |
StringBuffer |
|---|---|---|
| 可变性 | 不可变 (Immutable) | 可变 (Mutable) |
| 线程安全 | 不安全 (非同步) | 安全 (同步) |
| 性能 | 频繁修改时性能差,因为每次修改都创建新对象 | 频繁修改时性能好,直接修改内部数组 |
| 内存 | 修改时会产生新的内存对象 | 修改时通常不产生新对象(扩容除外) |
| 使用场景 | 存储不变的文本,如常量、配置信息 | 需要频繁进行字符串拼接、修改的场景 |
一个简单的例子来展示区别:
// String 示例
String s1 = "Hello";
String s2 = s1 + " World";
// 这里 s1 没有变,而是创建了一个新的 String 对象 "Hello World" 赋值给 s2
// s1 仍然指向 "Hello"
// StringBuffer 示例
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
// 这里直接在 sb 对象的内部进行了修改
// sb 现在的内容就是 "Hello World"
StringBuffer 的常用方法
StringBuffer 提供了丰富的方法来操作字符串。
1 构造方法
// 1. 创建一个空的 StringBuffer,初始容量为 16
StringBuffer sb1 = new StringBuffer();
// 2. 创建一个指定容量的空的 StringBuffer
StringBuffer sb2 = new StringBuffer(32);
// 3. 创建一个包含指定字符串的 StringBuffer,初始容量为 16 + 字符串长度
StringBuffer sb3 = new StringBuffer("Hello");
2 追加和插入
append() 和 insert() 方法返回的是 StringBuffer 对象本身,这被称为“方法链”或“流式调用”。

StringBuffer sb = new StringBuffer("Start");
// append(): 在末尾追加
sb.append(" Java"); // sb 变为 "Start Java"
System.out.println(sb);
// insert(): 在指定位置插入
sb.insert(5, "Learning "); // 在索引 5 的位置插入 "Learning "
System.out.println(sb); // 输出 "Start Learning Java"
3 删除和替换
StringBuffer sb = new StringBuffer("Hello, Programming, World!");
// delete(int start, int end): 删除从 start 到 end-1 的字符
sb.delete(7, 19); // 删除 " Programming"
System.out.println(sb); // 输出 "Hello, World!"
// deleteCharAt(int index): 删除指定索引的字符
sb.deleteCharAt(5); // 删除索引 5 的字符 ','
System.out.println(sb); // 输出 "Hello World!"
// replace(int start, int end, String str): 替换从 start 到 end-1 的字符
sb.replace(6, 11, "Java"); // 将 "World" 替换为 "Java"
System.out.println(sb); // 输出 "Hello Java"
4 反转和截取
StringBuffer sb = new StringBuffer("ABCDEF");
// reverse(): 反转字符串
sb.reverse();
System.out.println(sb); // 输出 "FEDCBA"
// substring(int start): 从指定位置开始截取到末尾
String sub1 = sb.substring(2);
System.out.println(sub1); // 输出 "DCBA"
// substring(int start, int end): 截取从 start 到 end-1 的子串
String sub2 = sb.substring(1, 4);
System.out.println(sub2); // 输出 "EDC"
5 其他实用方法
StringBuffer sb = new StringBuffer("Java is great");
// capacity(): 获取当前容量(内部数组大小)
System.out.println("Capacity: " + sb.capacity()); // 输出 "Capacity: 27" (16 + "Java is great".length())
// length(): 获取当前字符串长度
System.out.println("Length: " + sb.length()); // 输出 "Length: 13"
// charAt(int index): 获取指定索引的字符
char c = sb.charAt(0);
System.out.println("First char: " + c); // 输出 "First char: J"
// setCharAt(int index, char ch): 设置指定索引的字符
sb.setCharAt(0, 'j');
System.out.println(sb); // 输出 "java is great"
// toString(): 转换为不可变的 String 对象
String finalString = sb.toString();
System.out.println("Final String: " + finalString);
System.out.println("Is finalString a String? " + (finalString instanceof String)); // true
StringBuffer vs. StringBuilder
这是面试中非常经典的问题。
| 特性 | StringBuffer |
StringBuilder |
|---|---|---|
| 线程安全 | 是 (方法被 synchronized 修饰) |
否 |
| 性能 | 稍慢 (因为有同步开销) | 更快 (无同步开销) |
| 出现版本 | Java 1.0 | Java 5 |
| 使用建议 | 多线程环境 | 单线程环境 (绝大多数情况) |
如何选择?
- 默认使用
StringBuilder:在现代 Java 开发中,绝大多数字符串操作都在单线程中进行。StringBuilder因为其更高的性能,是首选。 - 仅在需要线程安全时使用
StringBuffer:如果你的字符串操作会被多个线程同时访问和修改,并且你需要保证数据的一致性,那么必须使用StringBuffer,这种情况相对较少。
性能对比示例
下面是一个简单的性能测试,展示 StringBuilder 在循环拼接时的巨大优势。
public class PerformanceTest {
public static void main(String[] args) {
int iterations = 100000;
// 测试 String
long startTime = System.currentTimeMillis();
String str = "";
for (int i = 0; i < iterations; i++) {
str += i; // 每次循环都创建一个新的 String 对象
}
long endTime = System.currentTimeMillis();
System.out.println("String拼接耗时: " + (endTime - startTime) + " ms");
// 测试 StringBuffer
startTime = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < iterations; i++) {
sb.append(i); // 在同一个对象上操作
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer拼接耗时: " + (endTime - startTime) + " ms");
// 测试 StringBuilder
startTime = System.currentTimeMillis();
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sBuilder.append(i); // 在同一个对象上操作,无同步开销
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder拼接耗时: " + (endTime - startTime) + " ms");
}
}
运行结果(可能因机器不同而异):

String拼接耗时: 4562 ms
StringBuffer拼接耗时: 15 ms
StringBuilder拼接耗时: 8 ms
从结果可以清晰地看到,String 的拼接方式在循环中性能极差,而 StringBuilder 是最快的,StringBuffer 略慢于 StringBuilder。
StringBuffer是 Java 提供的一个可变、线程安全的字符串操作类。- 它的核心价值在于可以高效地修改字符串内容,避免了
String类因不可变性带来的性能问题。 - 由于其同步特性,
StringBuffer的性能开销比StringBuilder大。 - 在日常的单线程编程中,优先使用
StringBuilder,只有在明确需要处理多线程并发修改字符串的场景下,才考虑使用StringBuffer。
