什么是 StringBuffer?
StringBuffer(字符串缓冲区)是 Java 中一个用于可变字符串操作的类,与 Java 中另一个核心的字符串类 String 不同,String 对象一旦创建,其内容(指向的字符序列)就不能被改变,任何对 String 的修改操作(如 拼接)都会生成一个新的 String 对象,这在频繁进行字符串修改的场景下会导致大量的内存开销和性能损耗。

StringBuffer 则解决了这个问题,它内部维护了一个可变的字符数组,当你对 StringBuffer 对象进行修改(如追加、插入、删除等)时,操作的是这个内部的字符数组,而不是创建新的对象。StringBuffer 是线程安全的,但这也意味着它的性能开销比非线程安全的版本要大。
StringBuffer vs String vs StringBuilder
理解 StringBuffer 的最佳方式是将其与 String 和 StringBuilder 进行比较。
| 特性 | String |
StringBuffer |
StringBuilder |
|---|---|---|---|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 线程安全(其不可变性本身就是安全的) | 线程安全 (所有 public 方法都同步) |
非线程安全 |
| 性能 | 频繁修改时性能差 | 性能较好,但有同步开销 | 性能最好 (无同步开销) |
| 主要用途 | 存储和操作不会改变的字符串内容 | 多线程环境下进行字符串拼接和修改 | 单线程环境下进行大量字符串拼接和修改 |
| 出现版本 | Java 1.0 | Java 1.0 | Java 5 |
核心区别总结:
StringvsStringBuffer/StringBuilder:String不可变,StringBuffer和StringBuilder可变。StringBuffervsStringBuilder:StringBuffer是线程安全的,方法被synchronized关键字修饰,而StringBuilder不是,在单线程程序中,StringBuilder的速度通常比StringBuffer快。
如何选择?

- 如果字符串内容不需要改变,使用
String。 - 如果字符串内容需要改变,并且在多线程环境中工作,使用
StringBuffer。 - 如果字符串内容需要改变,并且在单线程环境中工作(这是绝大多数情况),使用
StringBuilder以获得最佳性能。
StringBuffer 的核心构造方法和常用方法
构造方法
StringBuffer(): 创建一个空的字符串缓冲区,初始容量为16个字符。StringBuffer sb = new StringBuffer();
StringBuffer(int capacity): 创建一个指定初始容量的空字符串缓冲区。StringBuffer sb = new StringBuffer(32); // 初始容量为32
StringBuffer(String str): 创建一个字符串缓冲区,并将其内容初始化为指定的字符串str,缓冲区的容量为str.length() + 16。StringBuffer sb = new StringBuffer("Hello");
常用方法
StringBuffer 的方法都返回 this 引用,这支持了方法链式调用。
修改类方法:
append(String str): 将指定的字符串追加到此序列的末尾。sb.append(" World"); // sb 变为 "Hello World"insert(int offset, String str): 在此序列的指定位置插入字符串。sb.insert(5, ", Java"); // sb 变为 "Hello, Java World"
delete(int start, int end): 移除此序列的子字符串中的字符。sb.delete(5, 10); // 删除从索引5到10(不包含10)的字符,sb 变为 "Hello World"
deleteCharAt(int index): 移除此序列中指定位置的char。sb.deleteCharAt(0); // 删除索引0的字符 'H',sb 变为 "ello World"
replace(int start, int end, String str): 使用指定的字符串替换此序列的子字符串。sb.replace(0, 5, "Hi"); // 将索引0-5的字符替换为 "Hi",sb 变为 "Hi World"
reverse(): 将此序列用其反转形式取代。sb.reverse(); // sb 变为 "dlroW iH"
查询类方法:
charAt(int index): 返回此序列中指定索引处的char值。char c = sb.charAt(0); // c = 'd'
substring(int start): 返回一个新的String,它包含此序列中从start开始到末尾的子字符串。String sub = sb.substring(3); // sub = "roW iH"
substring(int start, int end): 返回一个新的String,它包含此序列中从start到end-1的子字符串。String sub = sb.substring(3, 6); // sub = "roW"
capacity(): 返回当前容量(内部字符数组的大小)。int cap = sb.capacity(); // cap 是当前分配的数组大小
length(): 返回长度(字符序列的实际长度)。int len = sb.length(); // len 是字符串 "dlroW iH" 的长度 8
toString(): 返回此序列的字符串表示。String result = sb.toString(); // result = "dlroW iH"
代码示例
示例1:基本用法
public class StringBufferDemo {
public static void main(String[] args) {
// 1. 创建一个 StringBuffer 对象
StringBuffer sb = new StringBuffer("Hello");
// 2. 追加内容
sb.append(" Java");
System.out.println("追加后: " + sb); // 输出: 追加后: Hello Java
// 3. 插入内容
sb.insert(5, ", ");
System.out.println("插入后: " + sb); // 输出: 插入后: Hello, Java
// 4. 替换内容
sb.replace(0, 5, "Hi");
System.out.println("替换后: " + sb); // 输出: 替换后: Hi, Java
// 5. 删除内容
sb.delete(0, 3);
System.out.println("删除后: " + sb); // 输出: 删除后: , Java
// 6. 反转
sb.reverse();
System.out.println("反转后: " + sb); // 输出: 反转后: avaJ ,,
// 7. 转换为 String
String finalString = sb.toString();
System.out.println("最终字符串: " + finalString); // 输出: 最终字符串: avaJ ,,
}
}
示例2:方法链式调用
public class StringBufferChainDemo {
public static void main(String[] args) {
// 链式调用:一次完成多个操作
String result = new StringBuffer("Start")
.append(" a")
.append(" new")
.append(" journey.")
.insert(5, " long") // 在 "Start" 和 " a" 之间插入 " long"
.toString();
System.out.println(result); // 输出: Start a long new journey.
}
}
性能考量:容量与扩容
StringBuffer 内部维护一个字符数组,当通过 append() 或 insert() 等方法添加的内容超过了当前容量时,StringBuffer 会自动进行扩容。

- 扩容机制:新的容量通常是 *`旧容量 2 + 2`**。
- 影响:频繁的扩容会涉及到数组的复制,这是一个相对耗时的操作,如果你能预估到最终字符串的大致长度,在创建
StringBuffer时指定一个合适的初始容量,可以有效提升性能。
// 错误的做法:频繁扩容
StringBuffer sb1 = new StringBuffer(); // 初始容量16
for (int i = 0; i < 100; i++) {
sb1.append("a"); // 可能会触发多次扩容
}
// 正确的做法:预估容量
StringBuffer sb2 = new StringBuffer(200); // 直接分配足够大的空间
for (int i = 0; i < 100; i++) {
sb2.append("a"); // 无需扩容
}
| 关键点 | 描述 |
|---|---|
| 核心作用 | 提供一个线程安全的、可变的字符串操作类。 |
与 String 的区别 |
String 不可变,每次修改都创建新对象;StringBuffer 可变,修改原对象。 |
与 StringBuilder 的区别 |
StringBuffer 线程安全(有同步开销),性能稍差;StringBuilder 非线程安全,性能最好。 |
| 使用场景 | 多线程环境下的字符串拼接、修改。 |
| 最佳实践 | 在单线程中,优先使用 StringBuilder,只有在明确需要线程安全时才使用 StringBuffer。 |
| 性能优化 | 如果能预估字符串长度,尽量在构造时指定初始容量以减少扩容操作。 |
掌握 StringBuffer 对于编写高效、健壮的 Java 程序,特别是在处理大量文本数据或动态构建字符串时,是非常重要的。
