String的核心在于不可变性、内存分布和编译/运行时行为:其final设计保证内容不可变,影响线程安全与HashMap键稳定性;字面量、new、intern导致对象存于常量池或堆中不同位置;编译期拼接优化与运行时StringBuilder生成逻辑决定对象数量。

直接抓住三个核心维度:不可变性、内存分布、编译与运行时行为。别背结论,要能画出内存图、推导对象数量、解释字节码背后的逻辑。
String 的不可变性不是口号,是设计契约
String 类被 final 修饰,内部 char[](JDK 9+ 是 byte[])也是 final,意味着一旦创建,内容无法修改。这不是“写代码时小心别改”,而是 JVM 层面禁止任何写操作——哪怕用反射强行修改 value 字段,也会破坏哈希缓存、破坏常量池复用、引发并发安全隐患。
关键影响有三点:
- 所有拼接、替换、截取操作都返回新对象,原字符串不受影响
- 天然线程安全:多线程读同一个 String 不需要同步
- 可作为 HashMap 键:hashCode 生成一次后永久缓存,查找稳定高效
字符串到底存在哪儿?分清字面量、new、intern 三类路径
同一段内容(如 "abc"),不同写法在内存中位置完全不同:
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- String s = "abc"; → 只查常量池:若存在则复用,否则新建并放入池中
- String s = new String("abc"); → 先确保常量池有 "abc",再在堆中新建一个对象(共可能 2 个对象)
- String s = new String("abc").intern(); → 若常量池已有 "abc",返回池中引用;否则将堆中对象引用加入池并返回
JDK 7 起,intern() 将堆中字符串的引用存入常量池(不再是复制),这是性能优化的关键点。
编译期 vs 运行时拼接,结果天差地别
编译器会主动优化纯字面量拼接,但只要掺入变量,就退化为运行时处理:
- "a" + "b" + "c" → 编译后等价于 "abc",只产生 1 个常量池对象
- "a" + "b" + s(s 是变量)→ 实际生成 StringBuilder.append(...) 代码,每次执行都新建对象
- final String s = "b"; "a" + s → 因 s 是编译期常量,仍按字面量优化,结果进常量池
面试常考:String s1 = "a" + "b"; String s2 = "ab"; System.out.println(s1 == s2); → true。而换成变量就 false。
String、StringBuilder、StringBuffer 别只记“线程安全”四个字
要讲清底层机制和适用边界:
- String:适合少量、不变更的文本(配置项、日志模板、Map键)
- StringBuilder:单线程高频拼接(如循环内构建 SQL、JSON),无锁,快;扩容策略是 old * 2 + 2,注意预设 capacity 避免反复拷贝
- StringBuffer:仅在真正多线程共享同一实例且需强一致性时使用(如老式 Servlet 共享缓冲区),synchronized 粒度是方法级,开销明确
误区提醒:不是“多线程就用 StringBuffer”,绝大多数场景用 StringBuilder + 局部变量,比共享 StringBuffer 更安全高效。

















