java字符串核心在于四条主线:对象创建决定内存归属(字面量→常量池,new→堆);==比地址,equals先判地址再逐字符比较内容;编译期拼接优化为常量池对象,运行时拼接生成堆对象;不可变性保障线程安全与哈希稳定,但频繁修改应选stringbuilder。

Java 字符串面试精细拆解,关键不是背结论,而是理清“对象怎么来、内容怎么比、内存怎么放、性能怎么选”这四条主线。下面从高频考点切入,直击本质。
字符串创建方式决定对象归属
两种写法,内存位置完全不同:
- 字面量赋值(如 String s = "abc";):JVM 先查字符串常量池,存在则复用,不存在则新建并放入池中;s 指向常量池地址。
- new String("abc"):一定在堆内存新建对象,同时常量池里也确保有"abc"(若没有则先放入),但堆中对象和常量池对象是两个独立实例。
所以 s1 == s2 为 true 仅当两者都是字面量且内容相同;只要有一个是 new 出来的,== 就几乎总是 false。
== 和 equals() 必须分清场景
这不是语法题,是内存模型题:
- == 比的是引用地址——它回答“是不是同一个对象?”
- equals() 是 String 重写后的方法——它先快速判断地址是否相同(同一对象直接返回 true),再逐字符比较内容(地址不同才走这步)。
常见陷阱:调用 s.equals(null) 不会空指针(因为 String.equals() 内部做了 null 判断),但 null.equals(s) 会抛 NullPointerException。
编译期优化与运行时拼接要划清界限
JVM 对字符串的“聪明处理”只发生在编译期确定的字面量拼接:
-
String s = "a" + "b" + "c";→ 编译后等价于String s = "abc";,仍走常量池,== 比较为 true。 -
String a = "a"; String s = a + "bc";→ 运行时拼接,底层用 StringBuilder,结果在堆中,== 比较为 false(除非显式 intern())。
注意:final String a = "a"; 会让 a + "bc" 也被当作编译期常量优化,这是少数能绕过运行时拼接的例外。
不可变性如何影响日常编码选择
String 不可变不是设计癖好,而是安全与性能的权衡结果:
- 作为 HashMap 的 key 安全——内容不变,hashCode 不变,桶位置不会漂移;
- 多线程共享无锁——不用同步,天然线程安全;
- 但频繁拼接(如循环内 s += "x")会产生大量中间对象,GC 压力大;这时应改用 StringBuilder(单线程)或 StringBuffer(多线程)。
记住:String 适合表示“值”,StringBuilder/StringBuffer 才适合做“构建过程”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











