java代码块执行顺序由jvm分阶段控制:静态部分(父→子)合并为按源码顺序执行,实例部分(父→子)混合字段赋值、实例块与构造器体依次执行,继承中父类构造时子类字段仍为默认值。

Java代码块的执行顺序不是“谁写在前面就先跑谁”,而是由JVM严格控制的分阶段流程。搞错顺序,轻则字段为null或0,重则类加载失败、构造器中调用子类方法引发空指针——这些问题往往在运行时才暴露,调试困难。
静态部分:只执行一次,但必须稳住顺序
静态变量赋值和static{}块会被JVM合并进一个叫<clinit></clinit>的字节码方法,按源码从上到下逐行执行。哪怕只是声明顺序颠倒,也可能导致前向引用错误或读到未初始化的值。
- 把依赖强的变量声明放在前面,比如
static Config cfg = load();要早于static Service s = create(cfg); - 避免在静态块里调用Spring Bean、访问数据库或发起HTTP请求——这些操作应延迟到
@PostConstruct或Holder模式中 - 用
javap -c YourClass检查<clinit></clinit>是否臃肿;超过20条指令,说明逻辑该拆了
实例部分:每次new都重走一遍,但父类永远先到位
实例变量显式赋值(如String name = "a";)和非静态代码块(即{ ... })地位等同,都在构造器体之前执行,且严格按源码顺序混合排列。它们和构造器一起构成“实例初始化阶段”。
- 多个实例块之间也按书写顺序执行,适合封装共用逻辑(如参数校验、日志打点)
- 不要在字段初始化表达式或实例块中调用非
final、非private方法——此时子类字段还是默认值,容易出错 - 想复用初始化逻辑?优先用
final字段 + 构造器参数注入,而不是靠代码块“悄悄赋值”
继承场景:父子链不可跳过,顺序固定为六步
创建一个子类对象,实际触发两轮初始化:类加载时的静态部分(父→子),对象创建时的实例部分(父→子)。完整顺序是:
- 父类静态变量赋值 + 静态块(从上到下)
- 子类静态变量赋值 + 静态块(从上到下)
- 父类实例变量赋值 + 实例块(从上到下)→ 父类构造器体
- 子类实例变量赋值 + 实例块(从上到下)→ 子类构造器体
这意味着:父类构造器执行时,子类所有实例字段仍是null或0。如果父类构造器中调用了被子类重写的方法,而该方法又访问了子类字段,结果必然是未初始化状态。
调试与验证:别猜,直接看执行流
最可靠的方式是在关键位置加带标识的日志,例如:
[S] static {} in Parent[I] instance block in Child[C] constructor start in Child
在main中首次访问某个静态字段(如System.out.println(Child.VERSION);),就能完整触发整个初始化链。观察输出是否符合预期顺序,比读文档更直观。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











