bigywater.tech
Java 内部原理 · 78

静态初始化顺序真实验证:父类静态块永远先于子类,只执行一次

静态块、静态字段、实例初始化块、构造器,四种初始化机制混在一条继承链上,顺序到底怎么排,这篇真实创建两个Child实例,把完整的14步执行顺序原样记录下来。

静态初始化真实只发生1次
真实验证:创建两个Child实例,Parent/Child的静态代码块和静态字段初始化真实只在第一次执行了一遍,第二次真实完全跳过
实例初始化真实每次都重来
真实验证:两次new Child()真实各自完整走了一遍"父实例块→父构造器→子实例块→子构造器",没有任何跳过

上一篇:字段隐藏真实验证

真实验证:创建两个实例的完整14步真实顺序

Parent/Child 两层继承链,各自都有静态代码块、静态字段初始化、实例初始化块、构造器四种初始化机制,真实创建两个Child实例记录完整日志。

StaticInitOrderDemo.java class Parent { static { log.add("Parent 静态代码块"); } static String staticField = initStaticField(); // 调用中会log { log.add("Parent 实例初始化块"); } Parent() { log.add("Parent 构造器"); } } class Child extends Parent { /* 同样四件套 */ } new Child(); new Child();
真实实测
1. main 开始,即将创建第一个 Child 实例
2. Parent 静态代码块
3. Parent 静态字段初始化
4. Child 静态代码块
5. Child 静态字段初始化
6. Parent 实例初始化块
7. Parent 构造器
8. Child 实例初始化块
9. Child 构造器
10. main 创建第二个 Child 实例
11. Parent 实例初始化块
12. Parent 构造器
13. Child 实例初始化块
14. Child 构造器

真实完整记录下的14步,清楚展示了两条独立的规则真实同时生效。

真实规则一:静态初始化只在类首次被使用时发生一次

对比第2-5步和10步之后的日志,能直接看出静态初始化真实不会重复。

真实证据。第一次 new Child() 触发了完整的类加载和初始化过程(步骤2-5):真实先是 Parent 的静态代码块,再是 Parent 的静态字段初始化,然后才是 Child 自己的静态代码块和静态字段——父类的静态初始化永远真实先于子类,这是 JVM 类加载机制里"先初始化父类,再初始化子类"规则的直接体现。但第二次 new Child()(步骤10之后)完全没有再出现"静态代码块"或"静态字段初始化"这几行日志——因为此时 Parent 和 Child 这两个类真实都已经处于"已初始化"状态,JVM 不会对同一个类重复执行静态初始化,不管这个类被 new 了多少次。

真实规则二:实例初始化每次 new 都会完整重来

对比步骤6-9和步骤11-14,两组顺序完全相同,证明实例级别的初始化是每次创建对象都要走一遍的。

真实结果显示第一次和第二次创建 Child 实例,都真实完整走了"Parent 实例初始化块 → Parent 构造器 → Child 实例初始化块 → Child 构造器"这四步,顺序完全一致,没有任何跳过。这和静态初始化形成鲜明对比:实例初始化块和构造器绑定的是"每一个对象实例"的生命周期,而不是"类"本身的生命周期,所以每次 new 都真实是一次完整、独立的初始化过程。规则本身也印证了 系列前面验证过的类初始化时机:类的静态初始化被延迟到真正"首次主动使用"时才触发,且真实只触发一次;而构造器执行(伴随实例初始化块)则是每次实例化都会重新走一遍的独立过程。

分步演示:14 步里哪些只发生一次,哪些每次都重来

对比第 2-5 步和第 10 步之后:第二次 new Child() 完全没有再出现"静态代码块"或"静态字段初始化",但实例初始化块和构造器又完整走了一遍——每一步都是真实运行时共享日志列表记录下的。

两次 new Child():静态初始化(2-5步)只真实发生在第一次,实例初始化(6-9、11-14步)每次都完整重来
这一步刚记录到的日志

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,14步执行顺序重复运行完全一致(这是JVM规范定义的确定性行为)。
    • 和类初始化时机那篇一起看,能拼出"类什么时候被初始化"和"初始化时内部各部分的真实顺序"这两层完整的知识。
    • 没有涉及:接口中的静态字段初始化时机(接口没有静态代码块,但常量字段有初始化表达式时的真实处理方式)、多线程并发首次访问同一个类触发初始化时JVM如何保证只执行一次(真实依赖类加载锁)——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号