Java 子类继承父类的执行顺序深度解析

码头散步
码头散步
发布时间:2026-10-14| 5人浏览过
原创

在 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 虚拟机的内部逻辑:

  1. 静态程序块:类加载时执行,仅一次。
  2. 主方法:程序入口,此时静态变量已初始化。
  3. 方法体(普通代码块):每次创建对象时,在构造方法之前执行。
  4. 构造方法:完成对象内存分配后的初始化。
  5. 具体业务方法:如 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 的处理步骤:

  1. 父类静态程序块:优先于子类加载。
  2. 子类静态程序块:父类加载完成后加载。
  3. 子类主方法开始:输出 England ,因为父类静态块中将其修改为此值。
  4. 对象实例化过程(每次 new 均发生):
    • 父类普通代码块执行。
    • 父类构造方法执行(隐式调用 super())。
    • 子类普通代码块执行。
    • 子类构造方法执行。
  5. 多态方法调用: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 继承体系中的执行顺序可归纳为以下铁律:

  1. 加载阶段(静态):父类静态变量 & 静态块 → 子类静态变量 & 静态块。此阶段只发生一次。
  2. 初始化阶段(动态):父类普通代码块 → 父类构造方法 → 子类普通代码块 → 子类构造方法。每 new 一次执行一次。
  3. 调用阶段:方法遵循多态原则(运行期绑定),字段遵循静态绑定原则。

以上就是 Java 子类继承父类及程序运行顺序的详细内容,更多关于 Java 面向对象特性与底层机制的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。