new String("abc")在堆上新建对象,"abc"优先查字符串常量池(JDK 7+位于堆但逻辑独立),二者引用不同;String类final且字段private final,保障不可变性与线程安全及常量池可信。

String对象创建时到底分配了几块内存
Java里new String("abc")和"abc"行为完全不同:前者一定在堆上新建对象,后者优先查字符串常量池(位于方法区),命中就复用,不命中才在池中新建并返回引用。
关键点在于——常量池本身是运行时常量池的一部分,JDK 7+后已移到堆中,但逻辑上仍独立管理;而new出来的String永远在堆上,哪怕内容相同,也和池中对象不是同一个引用。
-
"abc" == "abc"→true(字面量共享同一池内实例) -
new String("abc") == "abc"→false(堆对象 vs 池中对象) -
new String("abc").equals("abc")→true(值相等,equals重写了)
为什么String类被设计成final且字段private final
不可变性不是靠“程序员自觉”实现的,而是语言层强制保障:类被final禁止继承,核心字段如value是private final char[](JDK 9+改为byte[]),且所有可能修改内容的方法(如substring、concat)都返回新对象,绝不复用原value数组。
这么做最直接的好处是线程安全——多个线程读同一个String无需同步;更深层的是为常量池提供信任基础:如果池中字符串能被中途改写,整个复用机制就崩了。
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 别试图用反射修改
String.value,JDK 9+后字段名/类型都变了,且HotSpot有额外校验 -
StringBuffer和StringBuilder可变,但它们不进常量池,也不参与==比较优化 - 编译期确定的字符串拼接(如
"a" + "b")会被javac优化为单个字面量,直接进池
intern()方法什么时候有用,又为什么容易误用
intern()的作用是:若常量池已有相同内容的字符串,返回池中引用;否则把当前字符串加入池并返回其引用。它本质是手动触发“入池”,但代价不小——要遍历池做内容比对,JDK 7+后虽移到堆但仍需同步。
典型适用场景只有两个:大量重复字符串(如解析CSV的列名)、或必须用==做快速判断的极低延迟路径。其他情况基本是过早优化,甚至引入性能负收益。
-
new String("hello").intern() == "hello"→true(强制入池后与字面量指向同一对象) - 频繁调用
intern()可能撑爆元空间(JDK 8)或堆(JDK 7+),尤其处理用户输入时风险极高 - 注意
intern()返回的是池中引用,原对象仍存在堆上,GC不会因调用它就回收原对象
字符串拼接用+还是StringBuilder,编译器到底怎么优化
编译器只对**编译期完全确定**的字符串拼接做优化:比如"a" + "b" + getConst()(其中getConst()是static final字符串),会合并为一个字面量;但只要含任意变量(哪怕只是String s = "x"; s + "y"),javac就生成StringBuilder调用。
所以日常写+完全没问题,真正该手动用StringBuilder的,是循环内拼接、或明确知道要拼几十次以上且无法预估长度的场景。
for (int i = 0; i → 编译后等价于每次新建<code>StringBuilder,效率差-
String s = "a" + "b" + "c";→ 编译期直接变成"abc",无运行时开销 - JDK 9+对
StringConcatFactory做了优化,但底层仍是生成高效字节码,不改变“变量参与即不优化”的原则
真正容易被忽略的是:字符串不可变带来的内存压力往往不在“改不了”,而在“每次改都得多占一份内存”。尤其在流式处理、日志拼接、JSON序列化等场景,过度依赖+或没控制好intern()范围,会让GC频繁光顾老年代。

















