Java super 关键字详解教程

深入理解 Java 中 super 关键字的用法、原理与最佳实践


目录


一、super 关键字概述

1.1 什么是 super

super 是 Java 中的一个引用变量,指向当前对象的直接父类(超类)。它主要用于在子类中访问父类的成员(属性、方法、构造器)。

1.2 为什么需要 super

在 Java 继承体系中,子类可能会:

  1. 隐藏父类的成员变量(同名变量)
  2. 重写父类的方法(同名同参方法)
  3. 扩展父类的构造器逻辑

此时如果还想访问父类中被隐藏或重写的成员,就需要 super 关键字来"穿透"当前类,找到父类的版本。

1.3 super 的本质

graph TD A[子类对象内存结构] --> B[子类独有成员] A --> C[父类继承成员] C --> D[super 引用指向] A --> E[this 引用指向] D -->|访问父类成员| F[父类成员变量] D -->|调用父类方法| G[父类方法] D -->|调用父类构造器| H[父类构造器] E -->|访问本类成员| I[本类成员] E -->|调用本类方法| J[本类方法]

关键点:super 不是对象的引用,而是编译器提供的特殊关键字,用于在编译期确定要访问父类的哪个成员。


二、super 的三种基本用法

2.1 用法总览

序号 用法 语法 说明
1 访问父类成员变量 super.变量名 访问被子类隐藏的父类属性
2 调用父类方法 super.方法名(参数) 调用被子类重写的父类方法
3 调用父类构造器 super(参数) 或 super() 调用父类的构造器初始化父类部分

2.2 思维导图

mindmap root((super 关键字)) 访问成员变量 super.变量名 解决变量隐藏 调用成员方法 super.方法名 解决方法重写 调用构造器 super super参数列表 必须放在第一行

三、super 访问父类成员变量

3.1 什么是变量隐藏(Field Hiding)

当子类声明了与父类同名的成员变量时,父类的变量并不会被覆盖,而是被"隐藏"。此时通过子类对象访问该变量,默认访问的是子类的版本。

3.2 不使用 super 的问题

class Father {
    String name = "父亲";
    int age = 50;
}

class Son extends Father {
    String name = "儿子";  // 隐藏了父类的 name
    int age = 25;         // 隐藏了父类的 age

    public void print() {
        System.out.println(name);    // 输出 "儿子"
        System.out.println(age);     // 输出 25
    }
}

3.3 使用 super 访问父类被隐藏的变量

class Father {
    String name = "父亲";
    int age = 50;
}

class Son extends Father {
    String name = "儿子";
    int age = 25;

    public void print() {
        System.out.println("子类 name: " + name);          // 儿子
        System.out.println("父类 name: " + super.name);    // 父亲
        System.out.println("子类 age: " + age);             // 25
        System.out.println("父类 age: " + super.age);       // 50
    }
}

3.4 内存模型图

graph TD A[Son 对象内存] --> B[父类继承区域] A --> C[子类独有区域] B --> B1[父类 name 属性] B --> B2[父类 age 属性] C --> C1[子类 name 属性] C --> C2[子类 age 属性] C1 -- 通过 super 访问 --> B1 C2 -- 通过 super 访问 --> B2 C1 -- 通过 this 访问 --> C1

3.5 完整示例

class Animal {
    String type = "动物";
    String name = "通用名";
}

class Dog extends Animal {
    String name = "旺财";  // 隐藏父类 name

    void showInfo() {
        System.out.println("this.name = " + this.name);       // 旺财
        System.out.println("super.name = " + super.name);     // 通用名
        System.out.println("super.type = " + super.type);     // 动物
    }
}

public class TestSuperField {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.showInfo();
    }
}

输出结果:

this.name = 旺财
super.name = 通用名
super.type = 动物

四、super 调用父类方法

4.1 什么是方法重写(Override)

子类重新实现父类中已有的方法,称为方法重写。重写后,子类对象调用同名方法时,默认执行子类版本。

4.2 为什么需要调用父类被重写的方法

在以下场景中经常需要:

  1. 功能扩展:在父类原有功能基础上增加新逻辑
  2. 复用代码:避免重复实现父类已写好的核心逻辑
  3. 模板方法模式:父类定义流程,子类填充细节

4.3 基本语法

super.方法名(参数列表);

4.4 完整示例

class Vehicle {
    public void start() {
        System.out.println("交通工具启动:检查基本状态");
    }

    public void run() {
        System.out.println("交通工具行驶中");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("汽车启动前:检查油量、电瓶");
        super.start();  // 调用父类 start 方法
        System.out.println("汽车启动完成");
    }

    @Override
    public void run() {
        super.run();    // 调用父类 run 方法
        System.out.println("汽车以 80km/h 行驶");
    }
}

public class TestSuperMethod {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
        System.out.println("---");
        car.run();
    }
}

输出结果:

汽车启动前:检查油量、电瓶
交通工具启动:检查基本状态
汽车启动完成
---
交通工具行驶中
汽车以 80km/h 行驶

4.5 执行流程图

下面用一张完整的时序图展示 start() 与 run() 两个方法被调用时的完整执行链路。

sequenceDiagram participant Main as main方法 participant Car as Car对象 participant Vehicle as Vehicle父类 rect rgb(230, 245, 255) Note over Main,Vehicle: 第一次调用 car.start Main->>Car: 调用 start() Car->>Car: 输出 汽车启动前:检查油量、电瓶 Car->>Vehicle: super.start() Vehicle->>Vehicle: 输出 交通工具启动:检查基本状态 Vehicle-->>Car: 返回 Car->>Car: 输出 汽车启动完成 Car-->>Main: 返回 end rect rgb(255, 245, 230) Note over Main,Vehicle: 第二次调用 car.run Main->>Car: 调用 run() Car->>Vehicle: super.run() Vehicle->>Vehicle: 输出 交通工具行驶中 Vehicle-->>Car: 返回 Car->>Car: 输出 汽车以 80km/h 行驶 Car-->>Main: 返回 end

关键点:

  1. super 是在子类方法的执行过程中插入的调用,不是替代
  2. 调用 super.method() 时,JVM 会从方法区找到父类版本并执行
  3. 父类方法执行完毕后,控制权回到子类方法中 super 调用之后的语句
  4. 这种"先扩展、再复用父类、再收尾"的模式是方法重写中最常见的设计

4.6 链式调用(A → B → C)

class A {
    void show() {
        System.out.println("A.show");
    }
}

class B extends A {
    @Override
    void show() {
        super.show();  // 调用 A.show
        System.out.println("B.show");
    }
}

class C extends B {
    @Override
    void show() {
        super.show();  // 调用 B.show(B.show 内部会再调用 A.show)
        System.out.println("C.show");
    }
}

public class TestChain {
    public static void main(String[] args) {
        new C().show();
    }
}

输出结果:

A.show
B.show
C.show

五、super 调用父类构造器

5.1 为什么需要 super 调用构造器

在创建子类对象时,必须先初始化父类部分,再初始化子类部分。Java 通过 super 调用父类构造器来完成父类部分的初始化。

5.2 基本规则

规则 说明
必须在子类构造器第一行 否则编译报错
与 this() 互斥 二者只能出现一个
不写也会隐式调用 若子类构造器未显式调用,编译器自动添加 super()
必须存在匹配的构造器 父类必须有无参构造或显式调用的构造器

5.3 语法形式

// 形式一:调用父类无参构造器
super();

// 形式二:调用父类有参构造器
super(参数1, 参数2, ...);

5.4 完整示例

class Person {
    private String name;
    private int age;

    public Person() {
        System.out.println("Person 无参构造器");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person 有参构造器: " + name + ", " + age);
    }
}

class Student extends Person {
    private String studentId;

    public Student() {
        super();  // 显式调用父类无参构造器
        System.out.println("Student 无参构造器");
    }

    public Student(String name, int age, String studentId) {
        super(name, age);  // 调用父类有参构造器
        this.studentId = studentId;
        System.out.println("Student 有参构造器: " + studentId);
    }
}

public class TestSuperConstructor {
    public static void main(String[] args) {
        System.out.println("=== 创建 Student1 ===");
        Student s1 = new Student();

        System.out.println("\n=== 创建 Student2 ===");
        Student s2 = new Student("张三", 20, "S001");
    }
}

输出结果:

=== 创建 Student1 ===
Person 无参构造器
Student 无参构造器

=== 创建 Student2 ===
Person 有参构造器: 张三, 20
Student 有参构造器: S001

5.5 隐式调用 super()

class Parent {
    public Parent() {
        System.out.println("Parent 构造器");
    }
}

class Child extends Parent {
    public Child() {
        // 编译器会自动添加 super();
        System.out.println("Child 构造器");
    }
}

等价于:

class Child extends Parent {
    public Child() {
        super();  // 编译器隐式添加
        System.out.println("Child 构造器");
    }
}

5.6 创建对象的执行流程

sequenceDiagram participant Main as main方法 participant JVM as JVM participant Child as Child构造器 participant Parent as Parent构造器 Main->>JVM: new Child(参数) JVM->>JVM: 为 Child 对象分配内存 JVM->>Child: 执行 Child 构造器 Child->>Parent: super(参数) 第一行 Parent->>Parent: 初始化父类成员变量 Parent->>Parent: 执行父类构造器逻辑 Parent-->>Child: 返回 Child->>Child: 初始化子类成员变量 Child->>Child: 执行子类构造器剩余逻辑 Child-->>JVM: 返回 JVM-->>Main: 返回对象引用

5.7 典型错误:父类没有无参构造器

class Parent {
    private int id;

    public Parent(int id) {  // 只有有参构造器,没有无参
        this.id = id;
    }
}

class Child extends Parent {
    public Child() {
        // 编译错误!
        // 隐式 super() 不存在匹配的父类构造器
        System.out.println("Child");
    }
}

解决方法:

class Child extends Parent {
    public Child() {
        super(0);  // 显式调用父类有参构造器
        System.out.println("Child");
    }
}

5.8 this() 与 super() 的互斥

class Animal {
    String name;
    int age;

    public Animal() {
        System.out.println("Animal 无参");
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Animal 有参");
    }
}

class Dog extends Animal {
    String breed;

    public Dog() {
        this("未知", 0, "土狗");  // 调用本类其他构造器
        // 编译错误:this() 和 super() 只能出现一个
        // super();  ← 不能同时存在
    }

    public Dog(String name, int age, String breed) {
        super(name, age);  // 通过本类构造器间接调用父类
        this.breed = breed;
        System.out.println("Dog 有参: " + breed);
    }
}

图示:

graph TD A[new Dog] --> B[Dog 无参构造器] B --> C[this 有参调用] C --> D[Dog 有参构造器] D --> E[super 父类调用] E --> F[Animal 有参构造器] F --> G[Animal 初始化完成] G --> D D --> C C --> B B --> H[Dog 初始化完成]

六、super 与 this 的对比

6.1 核心区别

对比项 this super
含义 当前对象的引用 父类空间的引用
访问范围 本类成员 父类成员
构造器位置 必须放在第一行 必须放在第一行
互斥关系 不能与 this() 同存 不能与 super() 同存
能否共存 可以一起用:先 super() 再 this.成员 可以一起用:先 super() 再 this.成员
静态方法中 可以使用(不推荐) 不能使用

6.2 详细对比表

特性 this super
类型 引用变量 关键字
指向 当前对象 父类对象部分
调用本类构造器 this(参数) 不支持
调用父类构造器 不支持 super(参数)
访问本类成员 this.成员 不支持
访问父类成员 不直接支持 super.成员
是否能用于 static 可以但不推荐 禁止

6.3 思维导图对比

graph TB subgraph this_相关[this 引用] T1[this.成员变量] T2[this.成员方法] T3[this 参数列表] T4[this 调用本类构造器] end subgraph super_相关[super 引用] S1[super.成员变量] S2[super.成员方法] S3[super 参数列表] S4[super 调用父类构造器] end T1 -.对比.-> S1 T2 -.对比.-> S2 T3 -.对比.-> S3 T4 -.对比.-> S4

6.4 综合示例

class Base {
    int x = 10;

    Base() {
        System.out.println("Base 构造器");
    }

    Base(int x) {
        this.x = x;
        System.out.println("Base(int) 构造器");
    }

    void show() {
        System.out.println("Base.show, x = " + x);
    }
}

class Sub extends Base {
    int x = 20;  // 隐藏父类 x

    Sub() {
        super();  // 调用父类无参构造器
        System.out.println("Sub 构造器");
    }

    Sub(int x) {
        super(x);  // 调用父类有参构造器,传入的参数会赋给父类 x
        System.out.println("Sub(int) 构造器");
    }

    @Override
    void show() {
        System.out.println("Sub.show, this.x = " + this.x);
        System.out.println("Sub.show, super.x = " + super.x);
    }

    void test() {
        this.show();   // 调用本类 show
        super.show();  // 调用父类 show
    }
}

public class TestThisSuper {
    public static void main(String[] args) {
        Sub s1 = new Sub();
        s1.test();
        System.out.println("---");
        Sub s2 = new Sub(100);
        s2.test();
    }
}

输出结果:

Base 构造器
Sub 构造器
Sub.show, this.x = 20
Sub.show, super.x = 10
Base.show, x = 10
---
Base(int) 构造器
Sub(int) 构造器
Sub.show, this.x = 20
Sub.show, super.x = 100
Base.show, x = 100

七、使用 super 的注意事项

7.1 super 不能用于 static 方法

class Parent {
    int value = 10;
}

class Child extends Parent {
    int value = 20;

    public static void staticMethod() {
        // 编译错误!Cannot use super in a static context
        // System.out.println(super.value);
    }
}

原因:static 方法属于类,不属于任何对象,而 super 必须依赖具体对象才能指向父类部分。

7.2 super() 必须放在构造器第一行

class Parent {
    Parent() {
        System.out.println("Parent");
    }
}

class Child extends Parent {
    Child() {
        System.out.println("Child 第一行");
        super();  // 编译错误!Call to super must be first statement
    }
}

正确写法:

Child() {
    super();       // 必须在第一行
    System.out.println("Child 第一行");
}

7.3 super() 与 this() 不能共存

class Child extends Parent {
    Child() {
        super();         // 编译错误!两者只能选一个
        this("hello");   // 编译错误!
    }
}

解决方案:通过 this() 间接调用 super()。

class Child extends Parent {
    String data;

    Child() {
        this("default");  // 合法:this() 在第一行
    }

    Child(String data) {
        super();           // 合法:这里可以写 super()
        this.data = data;
    }
}

7.4 父类无默认构造器时必须显式调用

class Parent {
    Parent(int x) {  // 父类只有有参构造器
        System.out.println("Parent(int)");
    }
}

class Child extends Parent {
    Child() {
        super(10);  // 必须显式调用,否则编译错误
        System.out.println("Child");
    }
}

7.5 super 不会传递到祖父类构造器

class GrandParent {
    GrandParent() {
        System.out.println("GrandParent");
    }
}

class Parent extends GrandParent {
    Parent() {
        super();  // 显式调用 GrandParent
        System.out.println("Parent");
    }
}

class Child extends Parent {
    Child() {
        super();  // 显式调用 Parent
        System.out.println("Child");
    }
}

调用链:Child() → super() → Parent() → super() → GrandParent()

7.6 注意事项总结表

注意事项 是否允许 说明
static 方法中使用 super ❌ 不允许 static 与对象无关
super() 放在第二行 ❌ 不允许 必须第一行
构造器中同时出现 this() 和 super() ❌ 不允许 互斥
省略 super() 编译器自动添加 ✅ 允许 仅当父类有无参构造器
多级继承中 super 不跨级 ✅ 行为如此 每次只调用直接父类

八、super 在实际开发中的应用

8.1 应用场景总览

graph LR A[super 实际应用] --> B[1 构造器链式调用] A --> C[2 方法扩展复用] A --> D[3 模板方法模式] A --> E[4 访问被隐藏字段] A --> F[5 框架开发与扩展]

8.2 场景一:实体类的标准写法

class BaseEntity {
    private Long id;
    private Long createTime;
    private Long updateTime;

    public BaseEntity() {}

    public BaseEntity(Long id, Long createTime, Long updateTime) {
        this.id = id;
        this.createTime = createTime;
        this.updateTime = updateTime;
    }

    // getter / setter 省略
}

class User extends BaseEntity {
    private String username;
    private String email;

    public User() {
        super();
    }

    public User(Long id, String username, String email) {
        super(id, System.currentTimeMillis(), System.currentTimeMillis());
        this.username = username;
        this.email = email;
    }
}

8.3 场景二:方法扩展(常见业务需求)

// 父类提供基础保存逻辑
class BaseService<T> {
    public boolean save(T entity) {
        System.out.println("基础校验");
        System.out.println("通用日志记录");
        // 实际保存逻辑
        return true;
    }
}

// 子类扩展业务校验
class UserService extends BaseService<User> {
    @Override
    public boolean save(User entity) {
        if (entity.getUsername() == null) {
            System.out.println("用户名不能为空");
            return false;
        }
        return super.save(entity);  // 调用父类保存逻辑
    }
}

8.4 场景三:模板方法模式

abstract class DataProcessor {
    // 模板方法:定义处理流程
    public final void process() {
        readData();
        parseData();
        superHook();      // 钩子方法
        saveData();
    }

    void readData() {
        System.out.println("读取数据");
    }

    abstract void parseData();

    void superHook() {
        // 默认空实现,子类可选择性覆盖
    }

    void saveData() {
        System.out.println("保存数据");
    }
}

class JsonProcessor extends DataProcessor {
    @Override
    void parseData() {
        System.out.println("解析 JSON 数据");
    }

    @Override
    void superHook() {
        System.out.println("JSON 特殊预处理");
    }
}

8.5 场景四:调用父类构造器实现完整初始化

class Shape {
    private String color;

    public Shape(String color) {
        this.color = color;
        System.out.println("Shape 初始化: color=" + color);
    }

    public String getColor() { return color; }
}

class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);         // 必须先初始化父类
        this.radius = radius;
        System.out.println("Circle 初始化: radius=" + radius);
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }
}

8.6 场景五:多层继承的字段访问

class A {
    int value = 1;
}

class B extends A {
    int value = 2;  // 隐藏 A.value
}

class C extends B {
    int value = 3;  // 隐藏 B.value

    void show() {
        System.out.println("this.value = " + this.value);      // 3
        System.out.println("super.value = " + super.value);    // 2(B.value)
        // 无法直接通过 super 访问 A.value
    }
}

九、常见面试题汇总

9.1 面试题一:super 与 this 的区别

问题:请详细说明 this 和 super 的区别。

参考答案:

维度 this super
概念 当前对象引用 父类对象引用
访问 本类成员 父类成员
构造器 this(参数) super(参数)
静态方法 不推荐使用 禁止使用

9.2 面试题二:构造器执行顺序

问题:创建子类对象时,构造器的执行顺序是怎样的?

执行顺序:

graph TD A[1 父类静态代码块] --> B[2 子类静态代码块] B --> C[3 父类实例代码块] C --> D[4 父类构造器] D --> E[5 子类实例代码块] E --> F[6 子类构造器]

验证代码:

class Parent {
    static {
        System.out.println("Parent 静态代码块");
    }
    {
        System.out.println("Parent 实例代码块");
    }
    Parent() {
        System.out.println("Parent 构造器");
    }
}

class Child extends Parent {
    static {
        System.out.println("Child 静态代码块");
    }
    {
        System.out.println("Child 实例代码块");
    }
    Child() {
        super();
        System.out.println("Child 构造器");
    }
}

public class TestOrder {
    public static void main(String[] args) {
        new Child();
    }
}

输出:

Parent 静态代码块
Child 静态代码块
Parent 实例代码块
Parent 构造器
Child 实例代码块
Child 构造器

9.3 面试题三:super() 必须放在第一行的原因

问题:为什么 super() 必须放在子类构造器的第一行?

答案:

  1. 保证父类先初始化:Java 要求先完成父类部分的初始化,才能使用父类的成员
  2. 避免状态不一致:如果允许在父类初始化前访问父类成员,可能导致 NPE 或脏数据
  3. JVM 设计约束:JVM 在执行 <init> 方法时需要明确知道父类构造器的调用点

9.4 面试题四:变量隐藏与方法重写的对比

问题:成员变量的隐藏和方法重写在 super 访问上有什么区别?

答案:

特性 成员变量 方法
父类成员是否被覆盖 隐藏(共存) 重写(替换)
父类版本能否通过对象访问 不能(必须 super) 不能(必须 super)
编译期绑定 静态绑定 动态绑定(多态)

示例说明:

class Parent {
    int num = 10;
    void show() { System.out.println("Parent.show"); }
}

class Child extends Parent {
    int num = 20;

    @Override
    void show() {
        System.out.println("Child.show");
    }
}

public class TestBinding {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.num);    // 10(变量:编译期静态绑定)
        p.show();                     // Child.show(方法:运行期动态绑定)
    }
}

9.5 面试题五:父类构造器抛出异常

问题:父类构造器抛出异常,子类构造器需要如何处理?

答案:

class Parent {
    public Parent() throws Exception {
        throw new Exception("父类构造器异常");
    }
}

class Child extends Parent {
    public Child() throws Exception {
        super();  // 异常会传播
    }
}

子类构造器必须显式声明父类构造器抛出的异常,或在第一行用 try-catch 处理。

9.6 面试题六:super 能否访问父类的 private 成员

问题:能否通过 super 访问父类的 private 成员?

答案:

class Parent {
    private int privateField = 10;
    public int publicField = 20;
}

class Child extends Parent {
    void test() {
        // 编译错误!Cannot access private field of super
        // System.out.println(super.privateField);
        System.out.println(super.publicField);  // 合法
    }
}

解释:private 成员对子类不可见,super 关键字无法突破访问权限限制,只能访问 public、protected、同包 default 成员。

9.7 面试题七:super 的"穿透性"

问题:super 能否跨越多层继承访问祖父类的成员?

答案:

super 只指向直接父类,不能直接访问祖父类。但可以通过链式调用实现:

class GrandParent {
    void hello() { System.out.println("GrandParent"); }
}

class Parent extends GrandParent {
    @Override
    void hello() {
        super.hello();  // 调用 GrandParent.hello
        System.out.println("Parent");
    }
}

class Child extends Parent {
    @Override
    void hello() {
        super.hello();  // 调用 Parent.hello(内部会再调 GrandParent.hello)
        System.out.println("Child");
    }
}

十、总结与最佳实践

10.1 知识体系图

mindmap root((super 关键字)) 本质 编译期关键字 指向父类空间 用法 访问字段 解决变量隐藏 调用方法 解决方法重写 调用构造器 必须第一行 隐式调用规则 对比 this vs super 静态方法差异 应用 构造器链 方法扩展 模板模式 注意事项 不能用于static 互斥this 不可访问private

10.2 核心要点回顾

  1. super 是什么:编译期关键字,指向父类,用于访问父类被隐藏或重写的成员
  2. 三种用法:访问字段、调用方法、调用构造器
  3. 构造器规则:super() 必须放在第一行,与 this() 互斥,省略时隐式调用
  4. 与 this 区别:this 指本类,super 指父类
  5. static 限制:super 不能在 static 上下文使用
  6. 访问权限:super 不能突破 private 限制

10.3 最佳实践

实践 说明
显式调用 super 建议显式写 super(),代码更清晰
父类构造器保留无参 便于子类继承和框架反射
父类成员用 protected 给子类合理的访问权限
避免多层 super 链 继承层级不超过 3-4 层
方法重写调用 super 体现扩展而非完全替换
业务字段避免同名 减少 super 使用的复杂度

10.4 速记口诀

super 三用途:字段、方法、构造器
构造第一行:与 this 互不兼容
静态不能用:依赖对象才可行
private 不可见:访问权限要记清
变量是隐藏:方法是重写
多态看方法:变量看类型

10.5 学习路径建议

graph LR A[理解继承] --> B[掌握 super 基础] B --> C[深入 super 与 this] C --> D[实战构造器链] D --> E[理解多态与重写] E --> F[掌握设计模式应用] F --> G[阅读 JDK 源码]

附录:完整综合案例

// 完整的综合示例
class Animal {
    protected String name;
    protected int age;

    public Animal() {
        this.name = "未知";
        this.age = 0;
        System.out.println("Animal() 无参");
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Animal(name, age) 有参");
    }

    public void eat() {
        System.out.println(name + " 在吃东西");
    }

    public void sleep() {
        System.out.println(name + " 在睡觉");
    }
}

class Dog extends Animal {
    private String breed;

    public Dog() {
        super();  // 调用父类无参
        this.breed = "土狗";
        System.out.println("Dog() 无参");
    }

    public Dog(String name, int age, String breed) {
        super(name, age);  // 调用父类有参
        this.breed = breed;
        System.out.println("Dog(name, age, breed) 有参");
    }

    @Override
    public void eat() {
        super.eat();  // 调用父类 eat
        System.out.println(breed + " 品种的 " + name + " 啃骨头");
    }

    public void showInfo() {
        System.out.println("name = " + this.name);           // 本类
        System.out.println("name = " + super.name);          // 父类(继承自父类)
        System.out.println("breed = " + this.breed);         // 本类独有
    }
}

public class FinalDemo {
    public static void main(String[] args) {
        System.out.println("=== 创建 Dog1 ===");
        Dog d1 = new Dog();
        d1.eat();
        d1.showInfo();

        System.out.println("\n=== 创建 Dog2 ===");
        Dog d2 = new Dog("旺财", 3, "金毛");
        d2.eat();
    }
}

输出结果:

=== 创建 Dog1 ===
Animal() 无参
Dog() 无参
未知 在吃东西
土狗 品种的 未知 啃骨头
name = 未知
name = 未知
breed = 土狗

=== 创建 Dog2 ===
Animal(name, age) 有参
Dog(name, age, breed) 有参
旺财 在吃东西
金毛 品种的 旺财 啃骨头

posted @ 2026-10-08 19:29  RK5123153  阅读(6)  评论(0)    收藏  举报