题目
Java中String、StringBuilder和StringBuffer的区别与应用场景
信息
- 类型:问答
- 难度:⭐⭐
考点
字符串处理,不可变性,线程安全,性能优化
快速回答
核心区别:
- String:不可变对象,线程安全但频繁修改效率低
- StringBuilder:可变对象,非线程安全,单线程下性能最佳
- StringBuffer:可变对象,线程安全(synchronized方法),多线程场景适用
选择原则:
- 字符串不修改时用 String
- 单线程频繁修改用 StringBuilder
- 多线程环境修改用 StringBuffer
一、核心原理
1. String 的不可变性
String str = "Hello";
str += " World"; // 实际创建新对象"Hello World",原对象未修改- 每次修改都生成新对象,旧对象等待GC回收
- 导致频繁内存分配和垃圾回收,影响性能
2. StringBuilder/StringBuffer 的可变性
StringBuilder sb = new StringBuilder();
sb.append("Hello"); // 修改内部char[]数组
sb.append(" World"); // 数组扩容后继续修改- 内部维护可变char[]数组,修改时不创建新对象
- 初始容量16,自动扩容(新容量 = 旧容量*2 + 2)
二、线程安全对比
| 类名 | 线程安全 | 实现方式 |
|---|---|---|
| String | 是 | 不可变特性天然安全 |
| StringBuilder | 否 | 无同步控制 |
| StringBuffer | 是 | 关键方法添加synchronized |
三、性能测试示例
// 测试10万次拼接
long start = System.currentTimeMillis();
String s = "";
for (int i = 0; i < 100000; i++) {
s += i; // 每次循环创建新对象
}
System.out.println("String耗时: " + (System.currentTimeMillis()-start));
start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append(i); // 直接修改内部数组
}
System.out.println("StringBuilder耗时: " + (System.currentTimeMillis()-start));典型结果:String耗时 > 5000ms,StringBuilder耗时 < 10ms
四、最佳实践
- 字符串常量:直接使用 String(如:String url = "https://example.com")
- 循环内拼接:必须用 StringBuilder(避免在循环内用 + 拼接)
- 预分配容量:已知最终长度时初始化容量提升性能
// 已知最终长度约1000 StringBuilder sb = new StringBuilder(1000); - 多线程场景:使用 StringBuffer 或外部同步
// 方案1:使用StringBuffer StringBuffer safeBuffer = new StringBuffer(); // 方案2:外部同步StringBuilder StringBuilder sb = new StringBuilder(); synchronized(lockObject) { sb.append(data); }
五、常见错误
- 错误1:在循环中使用 + 拼接字符串
// 反例:产生大量临时对象 String result = ""; for (String str : list) { result += str; } - 错误2:多线程环境误用非线程安全的StringBuilder
- 错误3:不需要线程安全时使用StringBuffer(有同步开销)
六、扩展知识
- 字符串驻留(Interning):String.intern() 方法将字符串加入常量池
- Java 9 优化:String 内部存储改为 byte[] + 编码标记,节省内存
- 模式匹配:Java 17 的 switch 模式匹配可优化字符串分支处理