jvm创建对象时严格按“父类静态块→子类静态块→父类普通块→父类构造器→子类普通块→子类构造器”顺序执行;静态块仅加载时运行一次,普通块和构造器每次new都执行,隐式super()确保父类初始化优先。

要真正掌握子类对父类构造块逻辑的隐式继承,不能只背口诀,得靠代码跑出来——每次 new 对象时,JVM 都在背后自动执行一套固定流程。关键不是“记住顺序”,而是理解为什么这个顺序不可变、哪里容易出错、怎么用最简代码验证它。
静态代码块:类加载阶段的“一次性入场券”
静态代码块属于类级别,跟对象无关。只要 JVM 第一次主动使用该类(比如 new、调用静态方法、访问静态字段),就会触发类加载和初始化,此时:
- 先递归确保父类已加载并完成静态初始化(父类 static 块 → 子类 static 块)
- 同一类的多个 static 块按源码书写顺序执行
- 后续无论 new 多少次子类对象,static 块再也不会重跑
实战建议:在父类和子类各写一个带打印的 static 块,然后在 main 中先调用子类的静态方法(不 new 对象),再 new 子类实例。你会发现 static 块只输出一次,且父类先于子类。
普通代码块与构造器:每次 new 都走一遍的“初始化流水线”
非 static 的 {} 块(即普通代码块)属于实例级别,每次创建对象都会执行,位置在对应类构造器体之前、super() 调用之后(实际是编译器把它插在 super() 后面)。完整链路是:
- 为父类字段分配空间并设默认值(如 int=0,Object=null)
- 执行父类普通代码块(按声明顺序)
- 执行父类构造器主体
- 为子类字段赋初始值(如 int x = 5)
- 执行子类普通代码块
- 执行子类构造器主体
实战建议:在父类和子类中分别定义字段、普通代码块、构造器,并都加 System.out.println;不要写 super(),让编译器自动插入。运行后观察输出顺序,你会清晰看到“父块→父构→子块→子构”的刚性链条。
隐式 super() 是理解继承初始化的核心开关
子类构造器第一行永远隐含 super()(除非你显式写了 this() 或 super(…))。这不是语法糖,而是 JVM 强制保障父类部分被正确初始化的机制。一旦父类没有无参构造器,而子类又没写 super(…),编译直接报错。
常见陷阱:父类构造器里调用了被子类重写的方法。此时子类字段可能还是默认值(如 null 或 0),因为子类普通代码块和字段显式初始化还没执行到。这是典型的“过早暴露未初始化状态”问题。
实战建议:在父类构造器中调用一个虚方法(如 init()),子类重写它并打印 this.field。你会发现 field 是 null —— 这就是隐式调用链 + 初始化阶段分离导致的必然结果,不是 bug,是设计使然。
用最小差异对比法锁定执行逻辑
别写大项目验证,用两个类 + 三行 main 就够了。例如:
1. 先让父类有 static 块、普通块、构造器,子类只有普通块和构造器 → 看基础链路
2. 再给父类加一个带参构造器,删掉无参构造器,子类不写 super(…) → 编译失败,立刻明白隐式调用的约束
3. 最后在子类构造器第一行显式写 super(123),再加一句打印,确认 super() 确实是第一句
这种“改一行、跑一次、看输出”的方式,比读十遍文档更扎实。执行顺序不是抽象概念,是 stdout 里逐行出现的真实日志。










