【Java 每日一练 - Day3】String、StringBuffer、StringBuilder 的区别?性能对比 + 适用场景一次说透!
哈喽,刷题小分队集合!Day2 的 == 和 equals 考点是不是让你对 Java 的 “比较逻辑” 更清晰了?今天咱们攻克另一个面试高频题 ——String、StringBuffer、StringBuilder 的核心区别,这题不仅考定义,还常问性能对比和实际场景选择,赶紧往下看!
今日题目
- String 为什么是不可变的?底层实现原理是什么?
- StringBuffer 和 StringBuilder 的核心区别是什么?
- 三者的性能排序如何?分别适用于什么场景?
一、基础答案
- String:不可变字符序列
- 底层是 final char[] 数组(JDK9 后改为 byte[],按编码优化存储),final 修饰的数组无法修改引用,且 String 类没有提供修改数组内容的方法;
- 每次对 String 做拼接、替换等操作,都会新建一个 String 对象(原对象不变),如 String s = "a"; s += "b"; 会生成 3 个对象("a"、"b"、"ab")。
- StringBuffer:线程安全的可变字符序列
- 底层是 char[] 数组(默认容量 16),支持动态扩容;
- 所有修改方法(如 append()、insert())都加了 synchronized 锁,保证多线程环境下安全。
- StringBuilder:非线程安全的可变字符序列
- 底层实现和 StringBuffer 一致(char[] 数组 + 动态扩容);
- 未加锁,性能比 StringBuffer 高,但多线程环境下可能出现数据错乱。
- 核心区别总结表
| 特性 | String | StringBuffer | StringBuilder |
|--------------|-----------------------|-----------------------|-----------------------|
| 可变性 | 不可变 | 可变 | 可变 |
| 线程安全 | 安全(无修改方法) | 安全(synchronized 锁)| 不安全 |
| 底层实现 | final char []/byte [] | char [] | char [] |
| 性能 | 最低(频繁创建对象) | 中等(锁开销) | 最高(无锁) |
| 适用场景 | 字符串不频繁修改 | 多线程 + 字符串修改 | 单线程 + 字符串修改 |
二、深度拆解(面试加分项)
1. String 不可变的底层细节(JDK8 vs JDK9)
- JDK8 及之前:private final char value[],每个字符占 2 字节(UTF-16),无论中英文;
- JDK9 优化:private final byte[] value,搭配 coder 字段标记编码(LATIN1 占 1 字节,UTF-16 占 2 字节),英文场景更省内存。
不可变的好处:
- 线程安全:无需同步即可在多线程中共享;
- 缓存友好:字符串常量池可复用相同内容的对象(如 String s1="a"; String s2="a"; 指向同一个对象);
- 哈希值稳定:不可变对象的 hashCode 计算后可缓存,提升 HashMap 等集合的查询效率。
2. StringBuffer/StringBuilder 的扩容机制
默认初始容量为 16,当添加的内容超过当前容量时,扩容逻辑:
// 简化扩容源码(StringBuffer 和 StringBuilder 逻辑一致)
private void ensureCapacityInternal(int minimumCapacity) {
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity)); // 新容量 = 旧容量*2 + 2
}
}
优化建议:如果提前知道字符串长度(如拼接 100 个字符),可直接指定初始容量(如 new StringBuilder(100)),避免多次扩容(数组拷贝耗时)。
3. 性能对比实测(面试可直接说结论)
用 10 万次字符串拼接测试:
- String:约 3000ms(频繁创建对象,GC 开销大);
- StringBuffer:约 10ms(锁开销存在,但比 String 快几个数量级);
- StringBuilder:约 5ms(无锁,性能最优)。
结论:单线程场景优先用 StringBuilder,多线程用 StringBuffer,String 仅用于无需修改的字符串。
三、面试避坑提醒
- 避免在循环中用 String += 拼接字符串,会生成大量临时对象,建议用 StringBuilder;
- StringBuffer 的 synchronized 是对象锁,多线程操作同一个 StringBuffer 才安全,不同对象则无锁竞争;
- JDK9 后 String 底层改为 byte[],但不可变性的核心逻辑没变,面试时可主动提及这个优化,体现专业性;
- 注意:String s = "a" + "b" + "c"; 编译期会优化为 String s = "abc";,不会产生临时对象(编译器常量折叠)。
四、今日打卡
评论区留下你的答案:以下代码执行后,创建了几个 String 对象?✅
String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");
String s4 = s1 + "面试";
(提示:结合常量池和字符串拼接的优化逻辑分析)
文末预告
Day4 预告:Java 异常体系大梳理!Checked Exception 和 Unchecked Exception 的区别?try-catch-finally 如何正确使用?明天继续刷题~
更多推荐


所有评论(0)