准备阶段为static变量分配内存并设默认值,初始化阶段才执行赋值语句和static代码块;static final编译期常量直接内联,运行期常量则需初始化阶段赋值;声明顺序决定初始化顺序,影响最终值。

准备阶段和初始化阶段对静态变量的处理,本质是“占位”和“赋值”的分工:前者分配内存并设默认值,后者才执行你写的代码逻辑。
准备阶段:只做内存分配和默认值填充
这个阶段发生在类加载的连接环节,JVM为所有 static 变量 在方法区(或 Metaspace)分配内存,并按类型赋予 Java 规定的默认值:
- 数值类型(int/long/short/byte/char)→ 0
- 浮点类型(float/double)→ 0.0
- boolean → false
- 引用类型(Object、String 等)→ null
注意:此时不会执行任何赋值语句。比如 static int x = 5; 中的 5 不会出现,x 的值就是 0;static String s = "hello"; 中的 s 也是 null,不是 "hello"。
初始化阶段:执行你的显式逻辑
这个阶段真正运行类的 <clinit></clinit> 方法,也就是把所有 static 字段的赋值语句、static 代码块按顺序执行一遍。这时:
-
static int x = 5;→ x 被设为 5 -
static final String NAME = "foo".toLowerCase();→ 此时才调用 toLowerCase() 并赋值 -
static List<string> list = new ArrayList();</string>→ 此时才创建对象
只有在这个阶段,变量才拿到你代码里写的“真实初始值”。它由类首次被主动使用触发,比如 new 实例、调用 static 方法、访问非 final static 字段等。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
static final 编译期常量是个例外
如果 static final 字段是基本类型或 String,且用字面量直接初始化(如 public static final int PORT = 8080; 或 static final String MSG = "ok";),Javac 会在编译时把它当作常量内联到各处,跳过准备和初始化阶段——它的值在类加载前就“固化”了。
但只要初始化过程涉及运行期计算,哪怕只是加个括号、调用 valueOf()、拼接字符串,它就变成运行期常量,必须等到初始化阶段才赋值。
顺序错位会直接影响结果
静态字段声明顺序决定了初始化执行顺序。例如:
private static T t = new T(); // 先执行 private static int count = 2; // 后执行
构造器中对 count 的修改,会作用于初始化阶段尚未执行的 count = 2 语句之前,导致最终值不是 2。这说明:准备阶段设的默认值(0)被构造器改成了 1,再被 count = 2 覆盖 —— 顺序决定行为。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










