静态块和静态变量按源码从上到下顺序执行:谁在前谁先执行;父类静态成员优先于子类;初始化失败抛noclassdeffounderror;字节码中统一为方法。

静态块和静态变量的执行顺序,完全取决于它们在源码中出现的先后位置——谁写在上面,谁就先执行。这不是语法糖,而是 JVM 在类初始化阶段(<clinit></clinit> 方法)里严格按行扫描、依次插入指令的结果。
静态成员按源码顺序逐行执行
静态变量声明(含直接赋值)和静态代码块,在字节码中被合并进同一个 <clinit></clinit> 方法,JVM 按照源文件从上到下的顺序依次执行:
- 如果
static int x = getValue();写在static { ... }前面,那getValue()会先调用; - 反之,若静态块在前、变量在后,块内就不能引用该变量(编译报错:非法前向引用);
- 哪怕变量只是声明没赋值(如
static int y;),它也在准备阶段被设为默认值(0 / null / false),但“显式初始化”仍按位置执行。
静态初始化失败会导致类不可用
静态变量初始化表达式或静态块中一旦抛出未捕获异常(比如 NullPointerException 或 RuntimeException),整个 <clinit></clinit> 执行中断,该类进入“初始化失败”状态:
- 后续任何对该类的主动使用(访问字段、调用方法、实例化等)都会直接抛出
NoClassDefFoundError; - 不会重试初始化,也不会执行其后未执行的静态块或变量赋值;
- 常见陷阱:在静态工厂方法里做复杂初始化,却没兜住底层异常。
父子类之间存在强依赖顺序
子类首次被主动使用时,JVM 保证父类的静态成员一定先完成初始化:
- 先执行父类中所有静态变量赋值 + 静态块(按源码顺序);
- 再执行子类中所有静态变量赋值 + 静态块(同样按源码顺序);
- 这个顺序由类加载器双亲委派机制和 JVM 规范强制保障,无法绕过;
- 意味着子类静态块里可以安全使用父类的 public static 字段,但不能反过来。
实战中如何验证和调试
最直接的方式是加带标识的 System.out.println,配合类首次触发时机观察输出:
- 在 main 方法第一行之前访问某个静态字段(如
System.out.println(Child.flag);),就能触发完整类初始化流程; - 注意区分“类加载”(
ClassLoader.defineClass)和“类初始化”(<clinit></clinit>执行)——只有后者才执行静态块和变量赋值; - 用
javap -c 类名查看生成的<clinit></clinit>方法,能清晰看到语句被合并后的实际执行流。










