满足public static final、基本类型或string、编译期可确定值三个条件的静态常量会进入运行时常量池;否则在类初始化时存于堆中静态字段槽,不进常量池。

Java中类的静态常量(static final字段)是否进入方法区的运行时常量池,取决于它是不是编译期常量。不是所有static final都会进常量池,关键看值能否在编译时完全确定。
哪些静态常量会进入运行时常量池?
满足以下全部条件的static final字段,其字面量值会被编译器提取并写入.class文件的静态常量池,随后在类加载时载入方法区(JDK 8+为元空间)的运行时常量池:
- 声明为
public static final(访问修饰符不影响,但通常如此) - 类型是基本类型(
int、long、boolean、char等)或String - 初始化表达式是编译期可求值的字面量或常量表达式(如
1 + 2、"a" + "b")
例如:static final int MAX = 100; → 100进入静态常量池 → 类加载后存于运行时常量池(元空间)static final String NAME = "jvm"; → 字符串字面量"jvm"先入静态常量池,再在类加载时被 intern 到堆中的字符串常量池,同时其引用也保留在运行时常量池中。
哪些静态常量不会进运行时常量池?
只要初始化值依赖运行时计算,哪怕加了static final,也不会作为常量池项处理,而是在类初始化阶段(<clinit></clinit>)分配内存,存储在堆上的类变量区域(即Class对象的静态字段槽),和普通static变量一样:
static final int ID = new Random().nextInt();static final String MSG = System.getProperty("user.name");static final List<string> LIST = Arrays.asList("a", "b");</string>
这类字段在字节码中表现为对静态字段的getstatic指令访问,而非直接内联字面量;它们不占用运行时常量池空间,生命周期与类对象一致,但位置在堆,不在方法区/元空间。
一款AI工具,主要用于Monitor and clean up invalid Codex authentication files in CPA. Check quota status, disable files returning 401 errors, and perform dual verification before deletion.,适合需要提升相关任务效率的用户。
位置变化图:从编译到运行的三阶段流转
阶段1|编译期(.class生成)
→ 编译器扫描源码,识别出编译期常量,将其字面量(如42、"hello")写入.class文件的静态常量池(Constant Pool Table);符号引用(如类名、方法名)也一并存入。
阶段2|类加载(准备 & 解析)
→ JVM读取.class,将静态常量池内容整体复制到内存中的运行时常量池(位于方法区 / 元空间);
→ 字符串字面量进一步触发intern()逻辑:若堆中字符串常量池尚无该串,则将其实例放入堆,并在运行时常量池中记录其引用。
阶段3|运行期使用
→ 编译期常量被内联:代码中出现Demo.MAX的地方,字节码直接是bipush 100,不访问字段;
→ 非编译期常量则通过getstatic Demo.ID指令访问堆中对应的静态变量槽。
特别注意字符串的双重归属
static final String S = "abc"; 这个例子涉及两个池:
- 字面量
"abc"本身存在于运行时常量池(元空间)——作为常量池项,用于符号解析 - 该字符串对象实例存在于堆的字符串常量池(StringTable)——由
String.intern()机制保证唯一性
二者通过引用关联,但物理位置不同:前者是元空间里的结构化数据条目,后者是堆中真正的java.lang.String对象。










