准备阶段为静态变量分配内存并设默认值(如int为0、引用为null),不执行赋值语句或static块;初始化阶段才执行方法,按顺序完成static变量显式赋值和static代码块。

准备阶段只负责为静态变量分配内存并设默认值,不执行任何代码逻辑;真正赋程序员写的初始值,要等到初始化阶段。掌握这个分界,就抓住了静态变量初始化的核心。
准备阶段干了什么
JVM在类加载的准备阶段,为所有 static 字段 在方法区分配内存,并统一设置为该类型的默认零值:
- 数值类型(int/short/byte/char)→ 0
- long → 0L,float → 0.0f,double → 0.0d
- boolean → false
- 引用类型(如 String、Object)→ null
注意:这个过程由 JVM 自动完成,不运行 static {} 块,也不执行 static int x = 5; 中的 = 5 部分。
哪些变量例外——final static 编译期常量
被 static final 修饰、且值在编译期就能确定的变量,跳过零值阶段,直接在准备阶段写入初始值:
-
static final int MAX = 100;→ 准备阶段内存中就是 100 -
static final String s = "hello";→ 准备阶段就指向字符串常量池地址 - 但
static final Date d = new Date();不算——它依赖运行时对象创建,仍走初始化阶段
初始化阶段才真正“赋初值”
程序员在源码中写的显式初始值(比如 static int a = 10;),以及所有 static {} 块,都在初始化阶段按顺序执行:
- 先处理静态变量的显式赋值语句(按声明顺序)
- 再执行 static 块(多个则按书写顺序)
- 若存在依赖,如
static int b = a + 1;,此时a已完成赋值,取的是初始化阶段已设的值,不是准备阶段的 0
怎么验证这两个阶段的区别
最直接的方法是观察 static 块中打印变量的值:
- 如果输出 0 或 null,说明类被加载但尚未初始化(例如用
Class.forName(name, false)) - 如果输出预期的初始值(如 10),说明已触发完整初始化
- 配合
-XX:+TraceClassLoading可查看类加载各阶段触发时机










