静态变量在准备阶段赋默认值,初始化阶段才赋初始值;final static编译期常量例外,准备阶段即赋初始值。

静态变量在类加载的准备阶段被赋予默认值,而非初始值;初始值需等到初始化阶段才真正赋值。
准备阶段只设默认值,不执行任何代码
在 JVM 类加载的准备阶段,静态变量(static字段)会被分配内存并设置为该类型的默认值:
- int / short / byte / char → 0
- long → 0L
- float → 0.0f,double → 0.0d
- boolean → false
- 引用类型 → null
注意:此时不会执行 static {} 块,也不会执行静态变量声明时的显式赋值(如 static int x = 5; 中的 = 5 部分)。
初始值在初始化阶段才真正写入
静态变量的“初始值”(即程序员写的字面量或表达式结果)是在类初始化阶段(Initialization)完成的,按源码中声明和 static 块出现的顺序依次执行:
- 先处理静态变量的显式赋值语句(如 static int a = 10;)
- 再执行 static {} 块(可多个,按书写顺序)
- 若存在依赖关系(如 static int b = a + 1;),则以当前已赋值的状态为准
例如:
static int x = 10;
static { System.out.println(x); } // 输出 10,不是 0
说明该 static 块执行时,x 已完成显式赋值——这发生在初始化阶段,而非准备阶段。
final static 常量是特例:准备阶段就设初始值
被 static final 修饰、且编译期可确定值的常量(如字面量、常量表达式),会在准备阶段直接写入初始值,跳过初始化阶段的赋值逻辑:
- static final int MAX = 100; → 准备阶段内存中就是 100
- static final String s = "hello"; → 准备阶段即指向字符串常量池地址
- 但 static final Date d = new Date(); 不算编译期常量,仍走初始化阶段
验证时序的小技巧
可通过 -XX:+TraceClassLoading 查看类加载各阶段触发时机;更直观的是在 static 块中打印变量值,对比其是否为默认值或预期初始值:
- 若打印出 0 或 null,说明访问发生在准备后、初始化前(如通过 Class.forName(..., false) 触发加载但不初始化)
- 正常主动使用类(如调用静态方法、新建实例)会触发完整加载+初始化,看到的是初始值

















