StringBuffer
是 Java 中用于处理可变字符串的核心类之一。它与 String
和 StringBuilder
有密切关系,但在多线程环境下具有独特优势。
一、核心概念
1. 什么是 StringBuffer?
StringBuffer
是一个可变的字符序列,允许在不创建新对象的情况下修改字符串内容。- 它是线程安全的,所有公共方法都使用
synchronized
关键字修饰,适合多线程环境。 - 位于
java.lang
包中,无需导入。
2. 与 String 的区别
特性 | String | StringBuffer |
---|---|---|
可变性 | 不可变(immutable) | 可变(mutable) |
线程安全 | 是(因不可变) | 是(因同步方法) |
性能 | 拼接多时性能差 | 拼接效率高 |
使用场景 | 常量、配置、少量拼接 | 大量字符串操作、多线程环境 |
3. 与 StringBuilder 的区别
特性 | StringBuffer | StringBuilder |
---|---|---|
线程安全 | ✅ 是(同步) | ❌ 否(非同步) |
性能 | 相对较慢(加锁开销) | 更快 |
适用场景 | 多线程 | 单线程 |
✅ 结论:单线程优先用
StringBuilder
,多线程考虑StringBuffer
。
二、操作步骤(非常详细)
步骤 1:导入类(可选)
// StringBuffer 在 java.lang 包中,无需显式导入
// import java.lang.StringBuffer; // 可省略
步骤 2:创建 StringBuffer 对象
方法 1:无参构造函数(默认容量 16)
StringBuffer sb = new StringBuffer();
// 初始内容为空,容量为16个字符
方法 2:指定初始容量
StringBuffer sb = new StringBuffer(32);
// 容量为32,内容为空
方法 3:基于字符串初始化
StringBuffer sb = new StringBuffer("Hello");
// 内容为 "Hello",容量 = 16 + 字符串长度(5)= 21
💡 容量自动增长机制:当内容超过当前容量时,系统自动扩容(通常为原容量*2+2)。
步骤 3:添加内容(append)
append()
是最常用的方法,支持几乎所有数据类型。
StringBuffer sb = new StringBuffer("Hello");
// 添加字符串
sb.append(" World");
// 添加数字
sb.append(2025);
// 添加布尔值
sb.append(true);
// 添加字符
sb.append('!');
// 添加其他对象(调用 toString())
sb.append(new Object()); // 如 java.lang.Object@xxxx
System.out.println(sb.toString());
// 输出: Hello World2025true!java.lang.Object@xxxx
步骤 4:插入内容(insert)
在指定位置插入数据。
StringBuffer sb = new StringBuffer("HelloWorld");
// 在索引 5 处插入空格
sb.insert(5, " ");
// 在索引 0 处插入前缀
sb.insert(0, "Say: ");
// 插入整数
sb.insert(10, 2025);
System.out.println(sb.toString());
// 输出: Say: Hello 2025World
⚠️ 索引从 0 开始,超出范围会抛出
StringIndexOutOfBoundsException
。
步骤 5:替换内容(replace)
替换指定范围的字符。
StringBuffer sb = new StringBuffer("I love Java");
// 将索引 7 到 10(不包含)的内容替换为 "Python"
sb.replace(7, 10, "Python");
System.out.println(sb.toString());
// 输出: I love Python
步骤 6:删除内容(delete / deleteCharAt)
StringBuffer sb = new StringBuffer("abcdef");
// 删除索引 2 到 4(不包含)的字符
sb.delete(2, 4); // 删除 'c','d'
// 结果: abef
// 删除指定位置的单个字符
sb.deleteCharAt(0); // 删除 'a'
// 结果: bef
步骤 7:反转字符串(reverse)
StringBuffer sb = new StringBuffer("hello");
sb.reverse();
System.out.println(sb.toString()); // 输出: olleh
步骤 8:获取长度与容量
StringBuffer sb = new StringBuffer("Hi");
int length = sb.length(); // 实际字符数:2
int capacity = sb.capacity(); // 当前缓冲区容量:18(16+2)
System.out.println("长度: " + length);
System.out.println("容量: " + capacity);
步骤 9:转换为 String
StringBuffer sb = new StringBuffer("Final Result");
String result = sb.toString();
// 用于输出、存储或传递
三、常见错误
错误 | 原因 | 解决方案 |
---|---|---|
StringIndexOutOfBoundsException |
索引越界(负数或超出长度) | 检查索引是否在 [0, length()] 范围内 |
忘记调用 toString() |
直接打印 StringBuffer 对象 | 使用 .toString() 转换 |
在单线程中误用 StringBuffer | 性能浪费 | 单线程改用 StringBuilder |
频繁扩容影响性能 | 初始容量太小 | 预估大小并设置初始容量 |
四、注意事项
- ✅ 线程安全:多线程共享时无需额外同步。
- ❌ 不要过度使用:单线程环境下性能低于
StringBuilder
。 - 🔢 容量管理:频繁扩容影响性能,建议预设合理容量。
- 🔄 方法链调用:所有修改方法返回
this
,支持链式调用。sb.append("a").append("b").insert(0, "Start");
- 🧹 及时清理:长期持有大字符串缓冲区可能导致内存浪费。
五、使用技巧
技巧 1:链式编程
StringBuffer sb = new StringBuffer()
.append("Name: ").append(name)
.append(", Age: ").append(age)
.append(", Active: ").append(isActive);
技巧 2:预设容量提升性能
// 预估最终字符串长度约为 1000
StringBuffer sb = new StringBuffer(1000);
技巧 3:清空 StringBuffer
sb.setLength(0); // 清空内容,保留容量
// 或
sb.delete(0, sb.length());
技巧 4:检查是否为空
if (sb.length() == 0) {
System.out.println("缓冲区为空");
}
六、最佳实践与性能优化
✅ 最佳实践
场景 | 推荐做法 |
---|---|
单线程字符串拼接 | 使用 StringBuilder |
多线程共享字符串缓冲 | 使用 StringBuffer |
已知字符串最终长度 | 设置初始容量 |
多次拼接操作 | 避免使用 + ,使用 append() |
构建 SQL 或 JSON | 使用 StringBuffer /StringBuilder |
🚀 性能优化建议
避免默认构造函数(除非不确定大小)
// 不推荐 StringBuffer sb = new StringBuffer(); // 推荐:预估大小 StringBuffer sb = new StringBuffer(512);
减少扩容次数
- 每次扩容涉及数组复制,成本较高。
- 若知道大致长度,直接设置足够容量。
优先 StringBuilder(单线程)
// 单线程场景 StringBuilder sb = new StringBuilder(1024);
避免在循环中创建 StringBuffer
// ❌ 错误示范 for (int i = 0; i < 1000; i++) { StringBuffer temp = new StringBuffer(); temp.append(i); } // ✅ 正确做法 StringBuffer sb = new StringBuffer(4000); // 预估容量 for (int i = 0; i < 1000; i++) { sb.append(i); }
及时释放引用
sb = null; // 不再使用时置空,帮助 GC 回收
七、完整示例:构建用户信息字符串
public class StringBufferExample {
public static void main(String[] args) {
// 预设容量为 200
StringBuffer userInfo = new StringBuffer(200);
String name = "张三";
int age = 30;
boolean isActive = true;
String email = "zhangsan@example.com";
userInfo.append("=== 用户信息 ===\n")
.append("姓名: ").append(name).append("\n")
.append("年龄: ").append(age).append("\n")
.append("状态: ").append(isActive ? "激活" : "未激活").append("\n")
.append("邮箱: ").append(email).append("\n")
.append("生成时间: ").append(System.currentTimeMillis());
// 输出结果
System.out.println(userInfo.toString());
// 清空重用
userInfo.setLength(0);
userInfo.append("新内容...");
}
}
总结
要点 | 说明 |
---|---|
✅ 核心用途 | 可变字符串操作,线程安全 |
✅ 关键方法 | append() , insert() , delete() , reverse() , toString() |
✅ 性能提示 | 预设容量、单线程用 StringBuilder |
✅ 最佳实践 | 链式调用、避免频繁扩容、及时清理 |
📌 一句话总结:
多线程字符串拼接用
StringBuffer
,单线程用StringBuilder
,记得预设容量更高效!