java代码块执行顺序严格遵循jvm规范:静态内容按源码顺序在类首次主动使用时执行(父类优先),实例内容每次new时执行;static块中不可用this或依赖未初始化字段,异常会导致类初始化失败。

Java 中代码块执行顺序的核心在于类加载与实例化两个阶段的严格时序,不是靠“写在前面就先跑”这种直觉判断。静态块、实例块、构造方法、静态变量和实例变量的初始化,都遵循 JVM 规范定义的固定流程,一旦理解偏差,就容易写出依赖错误顺序的代码。
静态代码块只在类首次主动使用时执行一次
很多人误以为类一被加载(Loading 阶段)static 块就运行。其实它只在「初始化(Initialization)阶段」触发,且仅当类被首次主动使用时才进入该阶段。主动使用包括:
- new 创建实例
- 调用静态方法
- 访问非 final 的静态字段
- Class.forName("X")(默认 initialize=true)
- 子类初始化而父类尚未初始化
反例:访问 public static final int PORT = 8080; 这类编译期常量,不会触发类初始化,static 块也不会执行。
注意:ClassLoader.loadClass("X") 只加载不初始化,这是调试中常被忽略的关键区别。
静态块与静态变量按源码顺序混合执行
静态变量赋值(static int x = getValue();)和静态代码块是按文本从上到下交错执行的,并非“先全赋值再执行块”。这意味着:
- 若 getValue() 方法中引用了后面才声明的静态字段,该字段此时值为默认值(0 或 null),不是编译错误,但逻辑可能出错
- 父类的静态内容一定先于子类执行,顺序是:父类静态变量 → 父类 static 块 → 子类静态变量 → 子类 static 块
避免在 static 块中调用依赖未初始化静态字段的方法,或把有强依赖的初始化逻辑拆到单独的静态方法中,并确保调用位置在相关字段之后。
实例代码块和构造方法每次 new 都执行,但别在其中调用可重写方法
实例代码块({ ... })和构造方法在每次创建对象时都会执行,它们属于对象级别。常见陷阱是:
- 在构造方法中直接调用本类的非 final、非 private 方法
- 该方法被子类重写后,子类字段尚未初始化,却执行了子类逻辑,导致 NPE 或状态不一致
例如:父类构造器中调用 init(),而子类重写了 init() 并访问了子类的实例字段——此时字段还是默认值。
解决办法:将构造逻辑限制在 final 方法中,或延迟到对象完全构造后再调用可变行为。
不要在 static 块里创建本类实例或使用 this
static 块属于类初始化阶段,此时类还没完成加载,更不存在“当前实例”。以下写法编译直接报错:
static { this.doSomething(); } // 错误:非法引用非静态上下文
static { new MyClass(); } // 编译通过,但若构造过程依赖未初始化的静态字段,极易引发 ExceptionInInitializerError
如果 static 块中抛出未捕获异常(如 NullPointerException),JVM 会将该类标记为“初始化失败”,后续所有对该类的引用都会立即抛出 ExceptionInInitializerError,且无法重试。务必在 static 块中做最小化、确定性高的初始化,必要时加 try-catch 并记录日志。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











