java对象创建时代码块执行顺序为:先父类静态变量和static块(按源码顺序)、再子类静态部分;接着父类实例变量默认值→赋值/实例块→构造器,然后子类同理。该顺序影响字段赋值、方法调用安全及继承状态一致性。

Java 中对象创建时代码块执行顺序直接影响字段是否被正确赋值、方法调用是否安全、以及继承链中父子状态是否一致。搞错顺序,轻则字段为 null 或 0,重则构造器中调用子类方法引发空指针——而这类问题往往在运行时才暴露,调试成本高。
静态部分:类加载阶段只跑一次,但顺序决定依赖是否成立
静态变量赋值和 static{} 块会被 JVM 合并进一个 <clinit></clinit> 方法,按源码从上到下执行。父类静态部分一定先于子类执行,哪怕只写 new Child() 也会强制触发父类初始化。
- 若
static Service s = create(cfg);写在static Config cfg = load();后面,cfg就是null - 编译期常量(如
public static final int MAX = 100;)不触发初始化,直接内联,不会带动其他静态逻辑 - 避免在静态块里访问 Spring Bean、数据库或远程服务——这些应延迟到
@PostConstruct或 Holder 模式中
实例部分:每次 new 都走一遍,字段与代码块按书写位置混合执行
实例变量显式赋值(如 String name = "a";)和非静态代码块(即 { ... })地位完全等同,谁在源码里写在前面,谁就先执行。它们都在构造器体之前运行,且严格按声明顺序交替执行。
- 例如:先
int x = getValue();,再{ y = 20; },最后才进入构造器体 - 多个实例块也按定义顺序执行,适合封装校验、日志等共用逻辑
- 不要在字段初始化表达式或实例块中调用非
final、非private方法——此时子类字段仍是默认值,容易出错
继承场景:六步流水线不可跳过,父类构造时子类字段仍为空
执行 new Child() 会触发完整两阶段流程:先类初始化(静态),再对象初始化(实例)。实例部分的执行链条固定为:
- 父类实例变量设默认值 → 父类实例赋值 + 实例块(从上到下)→ 父类构造器体
- 子类实例变量设默认值 → 子类实例赋值 + 实例块(从上到下)→ 子类构造器体
- 这意味着:父类构造器中若调用了被子类重写的方法,该方法访问的子类字段仍是
null或0
验证方式:用日志锚定每个环节,别靠猜
最可靠的方式是在关键位置加带标识的日志,例如:
[S] static {} in Parent[I] instance block in Child[C] constructor start in Child
在 main 中首次访问某个静态字段(如 System.out.println(Child.VERSION);),就能完整触发整个初始化链。也可用 javap -c YourClass 查看 <clinit></clinit> 和 <init></init> 的实际字节码顺序。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











