Java 子类继承父类的执行顺序深度解析
在 Java 开发中,理解继承体系下的执行顺序至关重要。当子类继承父类时,无论是类的加载阶段还是对象的实例化阶段,都有严格的先后次序。本文将通过具体的代码案例,厘清静态代码块、普通代码块、构造方法以及父类与子类之间的执行依赖关系。
基础场景:单类内部执行顺序
首先观察一个包含静态变量、静态代码块、普通代码块和构造方法的父类结构。这类结构是理解复杂继承关系的基石。
public class TestStatic {
public static String name = "china";
// 普通代码块
{
System.out.println("========方法体========");
}
// 静态代码块
static {
name = "England";
System.out.println("========静态程序块======");
}
// 构造方法
TestStatic() {
System.out.println("=========构造方法========");
}
public static void main(String[] args) {
System.out.println("========主方法========" + name);
}
public void test() {
System.out.println("========测试方法=========");
}
}
如果在该类中通过 new 创建对象并调用方法,其典型输出顺序反映了 Java 虚拟机的内部逻辑:
- 静态程序块:类加载时执行,仅一次。
- 主方法:程序入口,此时静态变量已初始化。
- 方法体(普通代码块):每次创建对象时,在构造方法之前执行。
- 构造方法:完成对象内存分配后的初始化。
- 具体业务方法:如
test()。
核心解析:继承体系的完整执行流程
引入子类后,执行顺序会变得更加严谨。我们需要关注父类 TestStatic 和子类 TestExtendStatic 的交互。
// 子类定义
public class TestExtendStatic extends TestStatic {
// 注意:此处未重新定义静态变量name,因此沿用父类或父类修改后的值
// 子类普通代码块
{
System.out.println("========无名称方法体========");
}
// 子类静态代码块
static {
System.out.println("========子类静态程序块======");
}
// 子类构造方法
TestExtendStatic() {
System.out.println("=========子类构造方法========");
}
@Override
public void test() {
System.out.println("========子类测试方法=========");
}
public static void main(String[] args) {
System.out.println("========子类主方法========" + name);
TestStatic ts = new TestExtendStatic(); // 上转型对象
ts.test();
}
}
运行上述 main 方法时,控制台输出的精确顺序揭示了 JVM 的处理步骤:
- 父类静态程序块:优先于子类加载。
- 子类静态程序块:父类加载完成后加载。
- 子类主方法开始:输出
England,因为父类静态块中将其修改为此值。 - 对象实例化过程(每次 new 均发生):
- 父类普通代码块执行。
- 父类构造方法执行(隐式调用
super())。 - 子类普通代码块执行。
- 子类构造方法执行。
- 多态方法调用:
ts.test()由于动态绑定,执行子类重写的方法。
关键现象验证与排错
第1步:验证静态代码块的唯一性
目的:确认静态代码块是否在多次实例化中重复执行。
操作:在主方法中连续两次创建同一子类的对象。
TestStatic ts = new TestExtendStatic();
ts.test();
System.out.println("-------------------------");
ts = new TestExtendStatic();
ts.test();
结果:第一次创建时打印了所有静态块和普通块/构造函数日志;第二次创建时,不再打印静态块内容,直接从父类普通代码块开始。这证明静态块仅在类首次加载时执行。
第2步:对比直接实例化父类 vs 上转型对象
目的:明确“谁调用决定了什么代码被触发”这一误区,澄清类加载与对象创建的独立性。
操作:
- 情形 A:在子类 main 中写
TestStatic ts = new TestExtendStatic();。结果:触发子类静态块、子类对象初始化全过程。 - 情形 B:若代码直接在父类 main 中写
new TestStatic()。结果:不会触发子类的静态块,也不会实例化子类。
结论:JVM 是否需要加载子类,取决于程序中是否有指向该子类的引用或创建子类实例的操作,而与继承关系本身无关。只有触发了子类的实例化,才会走一遍“父静 -> 子静 -> 父非静 -> 父构 -> 子非静 -> 子构”的全套流程。
第3步:理解多态下的方法调用(上转型)
目的:解释为什么父类引用调用的是子类方法。
原理:TestStatic ts = new TestExtendStatic(); 创建了上转型对象。虽然引用类型是父类,但实际堆内存中的对象是子类实例。Java 的动态多态机制确保:成员变量看左边(编译期),成员方法看右边(运行期)。因此 ts.test() 执行的是子类重写后的 test() 方法。
简要总结
综上所述,Java 继承体系中的执行顺序可归纳为以下铁律:
- 加载阶段(静态):父类静态变量 & 静态块 → 子类静态变量 & 静态块。此阶段只发生一次。
- 初始化阶段(动态):父类普通代码块 → 父类构造方法 → 子类普通代码块 → 子类构造方法。每
new一次执行一次。 - 调用阶段:方法遵循多态原则(运行期绑定),字段遵循静态绑定原则。
以上就是 Java 子类继承父类及程序运行顺序的详细内容,更多关于 Java 面向对象特性与底层机制的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









