题目
Java中String、StringBuilder和StringBuffer的主要区别是什么?在什么场景下应该使用它们?
信息
- 类型:问答
- 难度:⭐
考点
字符串操作性能,不可变对象,线程安全
快速回答
主要区别:
- String:不可变对象,每次修改都会创建新对象,适合存储常量或少量字符串操作
- StringBuilder:可变对象,非线程安全,性能最高,适合单线程环境下的频繁字符串操作
- StringBuffer:可变对象,线程安全(synchronized方法),性能低于StringBuilder,适合多线程环境下的字符串操作
使用场景:优先使用StringBuilder(单线程),多线程共享时用StringBuffer,不修改的字符串用String。
解析
1. 核心区别
String 是不可变对象(immutable),底层使用final char数组存储数据。每次拼接、替换等操作都会创建新对象,频繁操作会产生大量临时对象,增加GC压力。
// String操作示例 - 性能低
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 每次循环创建新String对象
}StringBuilder 和 StringBuffer 都是可变对象(mutable),底层使用可扩容的char数组。修改时直接操作原数组,避免对象创建开销。
2. 性能对比
| 类型 | 可变性 | 线程安全 | 性能 |
|---|---|---|---|
| String | 不可变 | 线程安全 | 低(频繁操作时) |
| StringBuilder | 可变 | 非线程安全 | 高 |
| StringBuffer | 可变 | 线程安全 | 中(synchronized锁开销) |
性能测试示例(万次拼接耗时):
- String: ~450ms
- StringBuilder: ~2ms
- StringBuffer: ~3ms
3. 使用场景
- 使用String:
- 定义常量字符串(如配置信息)
- 不频繁修改的字符串操作
- 需要线程安全的只读场景
- 使用StringBuilder:
- 单线程下的字符串拼接(如SQL拼接)
- 循环体内的字符串处理
- 高性能要求的场景(99%的日常开发)
- 使用StringBuffer:
- 多线程共享的字符串操作(如全局日志缓冲)
- 需要同步修改的复杂业务逻辑
4. 最佳实践
// 正确用法示例
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i); // 只操作同一个对象
}
String result = sb.toString();- 预估大小:初始化时指定容量避免扩容(如
new StringBuilder(1024)) - 避免在循环中使用
+拼接字符串 - 多线程场景优先考虑线程局部变量(ThreadLocal)配合StringBuilder
5. 常见错误
- 在循环中使用
String += ...导致性能灾难 - 多线程误用StringBuilder引发数据不一致
- 未初始化合适容量导致频繁数组扩容
6. 扩展知识
- JVM优化:编译器会将部分
String +转换为StringBuilder操作(仅限于简单拼接) - 内存占用:String有内存优化(字符串常量池),StringBuilder/Buffer占用连续内存
- Java 9+:底层改用byte[]存储,减少内存消耗(Latin1字符节省50%空间)