java类初始化按源码顺序与继承层级执行静态变量和静态块,父类先于子类完成,仅首次主动使用时触发,异常导致类永久不可用。

Java 类初始化阶段执行静态变量和静态代码块,关键在于理解“谁先谁后”不是靠关键字优先级,而是由 JVM 严格按源码书写顺序 + 继承层级共同决定的单一执行流。
静态变量和静态块统一按源码顺序执行
它们被编译器合并进同一个
- 哪个静态变量声明得早,它的初始化表达式就先求值(比如 static String s = init(); 写在最上面,init() 就最先调用)
- 静态块也一样——写在第 3 行,就在第 3 行位置执行,不会因为加了 static 就跳到开头或结尾
- 若静态块出现在某个静态变量声明之前,块内不能读/写该变量(编译报错“非法前向引用”)
- 即使变量只声明没赋值(如 static List
data; ),它在准备阶段已被设为 null,但显式初始化动作仍卡在声明那行
父子类之间强制分层:父类全部完成,子类才开始
只要触发子类的首次主动使用(如调用子类静态方法、new 子类实例),JVM 就会先确保父类静态部分彻底执行完毕:
- 执行顺序固定为:Grandparent 静态变量 → Grandparent 静态块 → Parent 静态变量 → Parent 静态块 → Child 静态变量 → Child 静态块
- 父类静态块里可以安全访问父类自己的 public static 字段,但不能依赖子类任何静态状态(否则可能拿到默认值 null 或 0)
- 子类静态块中能读取父类已执行完的静态字段,值是确定的
- 这个顺序由双亲委派机制保障,无法绕过、倒置或跳过
哪些操作会真正触发初始化
静态变量和静态块只在类“首次主动使用”时执行一次,以下情况才算:
- 访问非 final static 基本类型常量的静态字段(如 System.out.println(Config.url);)
- 调用任意静态方法(哪怕方法体为空)
- 创建该类实例(new MyClass())
- 反射获取 Class 对象(Class.forName("MyClass"))
- 初始化子类(自动触发父类初始化)
- 运行包含 main() 方法的类
以下操作不会触发初始化:
- 仅声明引用(MyClass obj;)
- import 语句
- 访问 public static final int VERSION = 1; 这类编译期常量(值直接内联,不走运行时流程)
异常会导致类永久不可用
静态初始化不是容错过程:
- 任何一个静态变量赋值或静态块中抛出未捕获异常(如 NullPointerException),整个
立即中断 - 后续对该类所有主动使用(访问字段、调用方法、new 实例等)都会直接抛 NoClassDefFoundError
- 不会重试,也不会执行其后未执行的静态语句
- 建议在复杂静态初始化逻辑中主动捕获异常并提供 fallback,避免整类失效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











