Java变量初始化由JVM按固定阶段和代码位置严格驱动:静态部分在类首次主动使用时执行一次,按源码顺序合并进<clinit>方法;实例部分每次new都从父类到子类依次执行变量赋值、实例块和构造器。

Java变量初始化不是靠“谁更重要”来决定先后,而是由JVM按固定阶段和代码位置严格驱动。理解这个流程,比背口诀更能避开空指针、默认值误用、子类字段未初始化等常见问题。
静态部分:类首次主动使用时只跑一次
静态变量和static{}块被编译器合并进一个<clinit>方法,按源码从上到下顺序执行,且全局仅执行一次。触发时机是“类首次主动使用”,比如:
– 调用静态方法
– 访问非final静态字段
– 执行new MyClass()
– 反射加载Class对象
但访问public static final int VERSION = 1;这类编译期常量,不会触发初始化。
- 父类静态内容一定先于子类执行,哪怕你只写
new Child(),JVM也会先完成Parent的<clinit> - 静态块中若引用其后声明的静态变量,编译直接报错(非法前向引用)
- 任一静态初始化语句抛出未捕获异常,整个
<clinit>失败,后续所有对该类的访问都会抛NoClassDefFoundError
实例部分:每次new都重走一遍父→子链路
每次创建对象,JVM分配内存后立即执行实例初始化,顺序固定:
→ 先父类:按源码顺序执行父类实例变量赋值 + 实例块,再执行父类构造器
→ 再子类:同样顺序执行子类实例变量赋值 + 实例块,最后执行子类构造器
- 实例变量和{}块地位相同,编译后统一插入每个构造器开头(
super()之后) - 字段声明在前、方法调用在后?比如
int a = getValue();中getValue()返回b,而b = 10写在后面——此时b还是默认值0,不是10 - 构造器里调用可被重写的方法很危险:子类字段尚未初始化,可能读到0或null
验证执行流:加日志比猜更可靠
光看代码容易误判真实顺序。最有效方式是在关键位置加带标识的日志:
立即学习“Java免费学习笔记(深入)”;
- 静态变量初始化写成
static int x = logAndReturn(100);,而不是直接= 100 - 实例字段、实例块、构造器中分别打印如
"A init"、"init block"、"ctor start" - 运行
new Child(),典型输出是:Parent static → Child static → Parent init → Child init → Child ctor
编写建议:让初始化意图清晰可控
多数问题源于跨字段依赖或环境错位。实践中可这样规避:
- 静态变量尽量用字面量或无副作用表达式初始化,避免调用需实例上下文的方法
- 实例字段不依赖同级后声明的字段;必须关联时,统一挪到构造器里处理
- 复杂逻辑优先考虑静态工厂方法,把初始化过程显式暴露、集中控制
- 数组必须显式实例化(如
String[] cache = new String[100];),否则是null,不是空数组


















