String不可变因其类被final修饰且无法继承,核心字段private final byte[] value(JDK9+)或char[](JDK8-)既锁定引用又隔绝外部访问,所有修改操作均返回新对象。

从 final 类和 final 字段切入,直击不可变性本质
面试官问“String为什么不可变”,不能只答“因为用了final”。得拆到源码层:String类被final修饰,无法被继承;核心字段private final char[] value(JDK8及以前)或private final byte[] value + coder(JDK9+),既锁住引用,又封死外部访问路径;整个类没有一个public方法能修改value数组内容。所有操作如replace、substring、concat都新建对象——这不是设计选择,是代码强制约束的结果。
== 和 equals 的区别必须关联内存模型讲清楚
光说“==比地址、equals比内容”太浅。要结合常量池和堆内存讲行为差异:
-
String s1 = "abc"; String s2 = "abc";→s1 == s2为true,因字面量进常量池,复用同一对象 -
String s1 = new String("abc"); String s2 = new String("abc");→s1 == s2为false,两个堆中不同对象;但s1.equals(s2)为true,因equals重写后先判地址再逐字节/字符比内容 - JDK9+的equals还做了优化:先比coder是否一致,再按Latin-1或UTF-16方式比byte数组,避免无谓解码
字符串拼接背后的运行时真相
面试常问“s += "x"到底做了什么?”答案不能只写“等价于StringBuilder”。要拆三步:
- 编译期:若全是字面量(如
"a" + "b" + "c"),javac直接优化为"abc",走常量池 - 运行期:含变量时(如
str + "x"),编译成new StringBuilder().append(str).append("x").toString() - 注意陷阱:
toString()返回的是新String对象,不是复用原对象;频繁拼接会持续创建StringBuilder和新String,引发GC压力
intern() 方法要分JDK版本讲清行为差异
这个方法是高频陷阱点,必须区分JDK6与JDK7+:
立即学习“Java免费学习笔记(深入)”;
- JDK6:常量池在永久代,
intern()是把字符串拷贝一份进池;原对象还在堆里 - JDK7+:常量池移到堆中,
intern()是尝试将当前字符串对象注册进池——如果池中已有相同内容,则返回池中引用;否则把当前对象放入池并返回它 - 典型验证:
String s = new String("abc").intern(); String t = "abc";→ 在JDK7+中s == t为true;JDK6中为false


















