String vs StringBuilder vs StringBuffer深度解析

Java 面试核心:String vs StringBuilder vs StringBuffer 深度解析

一、三者核心区别一览

特性 String StringBuilder StringBuffer
可变性 不可变 (Immutable) 可变 (Mutable) 可变 (Mutable)
线程安全 安全(但无关,因为不可变) 不安全 安全(synchronized)
性能 低(每次操作创建新对象) 高(单线程最快) 中(同步带来开销)
使用场景 字符串常量、少量操作 单线程大量字符串拼接 多线程大量字符串拼接
JDK版本 1.0 1.5 1.0

二、String 的不可变性(Immutable)

为什么不可变?

java 复制代码
// 典型面试代码
String s = "hello";
s = s + " world";  // 创建了新对象,s指向新引用,"hello"被GC

不可变的好处:

  1. 字符串常量池复用 --- 节省内存
  2. HashCode 缓存 --- 适合作为 HashMap 的 Key
  3. 线程安全 --- 天然并发安全
  4. 安全 --- 防止参数被篡改(如网络传输、文件路径)

经典面试题

Q:以下代码创建了几个对象?

java 复制代码
String s1 = new String("abc");

A:2个 --- 堆中的 new String() 对象 + 常量池中的 "abc"
Q:String.intern() 的作用?
A: 将字符串放入常量池,返回常量池引用。用于手动控制字符串复用。


三、StringBuilder vs StringBuffer 源码级对比

1. 线程安全差异(核心考点)

java 复制代码
// StringBuffer:方法加 synchronized
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

// StringBuilder:无同步,裸奔
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

2. 扩容机制(两者相同)

java 复制代码
// 默认初始容量 16,扩容:新容量 = 旧容量 × 2 + 2
void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2;
    // ...
}

面试建议: 预估长度时用 new StringBuilder(100) 避免频繁扩容。


四、性能对比实测

操作 耗时 说明
String + 循环10000次 很慢 创建大量临时对象
StringBuilder 单线程 最快 无锁,直接操作字符数组
StringBuffer 单线程 较慢 同步锁开销
StringBuffer 多线程 安全 正确性优先

结论: 单线程无脑用 StringBuilder,多线程才考虑 StringBuffer。


五、高频面试题精编

Q1:为什么 String 设计成不可变的?

答: 安全(防篡改)、高效(常量池复用)、线程安全、HashCode 缓存。

Q2:+ 操作符底层用什么?

答: JDK8+ 编译器自动转为 StringBuilder.append(),但循环中会每次新建 Builder,应手动用 Builder。

Q3:StringBuilder 为什么快?

答: 1)可变字符数组,原地修改;2)无同步锁开销;3)扩容时批量复制。

Q4:什么场景必须用 StringBuffer?

答: 多线程并发修改同一字符串对象时(实际很少见,通常用并发容器或局部变量)。


六、最佳实践速记

场景 推荐选择
字符串常量、配置项 String
单线程字符串拼接(循环、流处理) StringBuilder ⭐
多线程共享可变字符串(罕见) StringBuffer
高并发日志、序列化 StringBuilder(局部变量)

七、总结

String 是不可变的常量,StringBuilder 是单线程的快刀,StringBuffer 是多线程的保险柜。现代 Java 开发,90% 场景用 StringBuilder,剩下 9% 用 String,1% 才用 StringBuffer。


相关推荐
学长毕设13 分钟前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
zzu123zsw1 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号2 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场3 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠3 小时前
泛微OAe9-后端二开异步调用
java
秋天的怀念_David4 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放4 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog4 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友
用户3721574261354 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java
小嘉丶学长5 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节