静态变量在类初始化阶段(initialization)完成初始化,且仅执行一次;按源码文本顺序依次执行静态字段赋值和静态代码块;被动引用不触发初始化,此时静态变量仍为默认值。

静态变量在类加载过程中完成初始化,具体发生在类初始化阶段(Initialization),而非加载(Loading)或连接(Linking)阶段。这个阶段由 JVM 主动触发,且仅执行一次,是静态变量真正被赋值、静态代码块被执行的关键时刻。
类初始化的触发条件
类初始化不是类加载一完成就立刻发生,而是满足以下任一条件时才启动:
- 首次主动使用该类:如创建类的实例(new 指令)
- 调用类的静态方法(非接口)
- 访问类的静态字段(被 final 修饰且已在编译期确定值的常量除外)
- 反射调用(如 Class.forName("xxx"))
- 初始化子类时,若父类尚未初始化,则先触发父类初始化
- JVM 启动时指定的主类(含 main 方法的类)
静态变量初始化的执行顺序
在类初始化阶段,JVM 按源码中声明的**文本顺序**,依次执行:
- 静态字段的赋值语句(如 static int x = 10;)
- 静态代码块(static { ... })
同级声明中,变量声明与静态块交错出现时,仍严格按书写顺序执行。例如:
static int a = 1;
static { System.out.println(a); } // 输出 1
static int b = a + 1; // b = 2
static { System.out.println(b); } // 输出 2
注意“被动引用”不触发初始化
以下情况属于被动引用,不会导致类初始化,静态变量也就不执行初始化:
- 通过子类引用父类的静态字段(final 静态常量除外)
- 通过数组定义引用类(如 MyClass[] arr;)
- 引用类的 constan pool 中的符号引用(如获取 Class 对象但未主动使用,MyClass.class 不触发初始化)
这意味着:静态变量此时仍为默认值(如 0、null),直到该类被主动使用。
与实例变量的本质区别
静态变量属于类本身,生命周期与类相同;实例变量属于对象,每次 new 才分配并初始化。关键差异在于:
- 静态变量初始化只发生一次,在类初始化阶段完成
- 实例变量初始化每次创建对象时都执行(在构造器执行前,按声明顺序进行)
- 静态变量可被所有实例共享,且可在无任何实例时通过类名直接访问
不复杂但容易忽略:静态变量的值是否已正确初始化,取决于该类是否已被 JVM 主动触发初始化,而不是它是否已被加载或链接。








