核心概念
特性 |
String |
StringBuffer |
可变性 |
❌ 不可变(Immutable) |
✅ 可变(Mutable) |
线程安全 |
✅ 天然线程安全(因不可变) |
✅ 线程安全(方法用 synchronized 修饰) |
性能 |
⚠️ 频繁修改时效率低(产生大量中间对象) |
✅ 频繁修改时效率高(原地修改) |
存储位置 |
字符串常量池 |
堆内存 |
适用场景 |
字符串不经常修改 |
字符串频繁修改(尤其多线程环境) |
操作步骤详解(StringBuffer)
1. 创建对象
// 空对象(初始容量16字符)
StringBuffer sb1 = new StringBuffer();
// 指定初始容量(避免频繁扩容)
StringBuffer sb2 = new StringBuffer(100);
// 用字符串初始化
StringBuffer sb3 = new StringBuffer("Hello");
2. 追加内容(append()
)
sb1.append("Java"); // 追加字符串
sb1.append(11); // 追加int → "Java11"
sb1.append('!'); // 追加char → "Java11!"
sb1.append(true); // 追加boolean → "Java11!true"
3. 插入内容(insert()
)
sb1.insert(4, "Script"); // 索引4处插入 → "JavaScript11!true"
4. 删除内容(delete()
/deleteCharAt()
)
sb1.delete(10, 15); // 删除[10,15) → "JavaScript"
sb1.deleteCharAt(9); // 删除索引9的字符 → "JavaScrip"
5. 替换内容(replace()
)
sb1.replace(0, 4, "Type"); // [0,4)替换为"Type" → "TypeScrip"
6. 反转字符串(reverse()
)
sb1.reverse(); // → "pirScpyT"
7. 转换为String(toString()
)
String result = sb1.toString(); // "pirScpyT"
8. 其他关键操作
int len = sb1.length(); // 获取长度
int cap = sb1.capacity(); // 获取当前容量
sb1.setLength(5); // 设置长度(截断/补空字符)
sb1.trimToSize(); // 释放多余容量(节省内存)
常见错误
- 混淆不可变性
String s = "A";
s.concat("B"); // 错误!s仍为"A"(需赋值:s = s.concat("B");)
- 误用String拼接大对象
// 错误:循环中String拼接产生大量垃圾对象
String s = "";
for (int i = 0; i < 10000; i++) {
s += i; // 每次循环new StringBuilder→拼接→toString
}
- 索引越界
StringBuffer sb = new StringBuffer("Hi");
sb.insert(5, "!"); // StringIndexOutOfBoundsException
注意事项
- 线程安全代价
StringBuffer
的同步锁在单线程下是性能损耗,此时应用 StringBuilder
(非线程安全,但速度更快)。
- 初始容量设置
预估最终长度并设置初始容量(如 new StringBuffer(1000)
),避免多次扩容(默认扩容:(旧容量+1)*2
)。
- 避免频繁
toString()
多次调用 toString()
会生成新String对象,应在最终需要时调用。
使用技巧
- 链式调用(因方法返回
this
)
sb.append("A").insert(1, "B").reverse();
- 清空缓冲区
sb.setLength(0); // 重用对象(避免new)
- 单线程优化
用 StringBuilder
替代(API相同,性能提升10%~15%)。
最佳实践与性能优化
场景选择
场景 |
推荐类 |
字符串常量(如配置Key) |
String |
单线程字符串频繁修改 |
StringBuilder |
多线程字符串频繁修改 |
StringBuffer |
性能关键点
- 拼接大字符串
// ✅ 正确做法(预分配容量)
StringBuffer sb = new StringBuffer(10000);
for (int i = 0; i < 5000; i++) {
sb.append(i); // 无额外对象产生
}
- 避免无谓转换
// ❌ 低效
String s = "A" + 1 + "B"; // 编译为 new StringBuilder().append("A").append(1).append("B")
// ✅ 直接使用StringBuilder/Buffer
sb.append("A").append(1).append("B");
- 复用对象
在循环外部创建 StringBuffer
,避免每次循环新建。
总结
决策因素 |
选择 |
字符串不修改 |
String |
单线程频繁修改 |
StringBuilder |
多线程频繁修改 |
StringBuffer |
内存敏感场景 |
预分配容量 + 复用 |
实践口诀:
“不变用String,单线程改用Builder,多线程改用Buffer,预分配容量效率高!”