常量池存字面量并复用地址,堆存new创建的对象实体,intern可将堆字符串加入常量池,拼接结果取决于编译期是否能确定全部内容。

理解 Java 中 String 常量池与堆内存的关系,关键在于抓住“谁存内容、谁管复用、谁负责生命周期”这三点。它们不是并列的独立区域,而是分工协作的存储机制:常量池专注复用,堆负责承载对象实体,而栈只存引用。
常量池:编译期确定的字符串“共享仓库”
字符串常量池(String Pool)从 JDK 7 起已物理位于堆内存中,但逻辑上仍是一个独立管理的区域——它不存任意对象,只收编“编译期就能确定”的字符串字面量,比如 "abc"、"hello world" 这类直接用双引号写的值。
- 每次遇到字面量,JVM 先查池中是否已有相同 Unicode 序列;有则复用地址,无则新建后放入
- 因此 String a = "test"; String b = "test"; 中,a 和 b 指向同一块内存,a == b 为 true
- 池中永远只保留一份相同内容的字符串,这是节省内存和加速比较的核心设计
堆内存:运行期对象的“实际落脚地”
所有通过 new String("...") 创建的 String 实例,一定在堆中分配新空间——哪怕常量池里早有同内容字符串,也会额外造一个堆对象。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 这个新对象内部的 char 数组(value 字段)可能复用常量池中的字符数据,但 String 对象本身是独立的
- 所以 String x = new String("test"); 后,x == "test" 是 false,因为一个是堆地址,一个是常量池引用
- 堆中对象由 GC 管理,生命周期取决于引用关系,不像常量池那样长期驻留
intern():打通堆与常量池的“手动桥梁”
String.intern() 是唯一能主动把堆中字符串“拉进”常量池的操作。
- 调用时,JVM 查常量池:若有相同内容,直接返回池中引用;若无,则把该字符串内容加入池,并返回新引用
- 因此 new String("abc").intern() == "abc" 恒为 true,因为右边字面量已在池中,左边调用 intern 后也指向池中同一份
- JDK 7+ 后效率更高,因常量池就在堆里,无需跨区域复制数据
拼接行为:看上下文决定去哪“落户”
字符串拼接不是统一走某条路,而是由编译期能否确定全部内容来决定:
- 纯字面量拼接(如 "ab" + "c"):编译器直接优化成 "abc",走常量池路径
- 含变量的拼接(如 "ab" + s):运行时通过 StringBuilder 构建,结果默认在堆中生成新 String
- 高频拼接建议显式用 StringBuilder,避免反复创建临时对象和堆压力
不复杂但容易忽略。核心就四句话:字面量进池、new 在堆、intern 可桥接、拼接看上下文。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










