Skip to content
第 17 / 250 章后端⏱ 10 分钟阅读

第 17 章:继承

学习目标

  • 理解继承的本质:代码复用 + 多态基础
  • 掌握 extends 关键字和 super 用法
  • 学会方法重写(Override)
  • 理解构造器链

一、什么是继承?

继承是子类复用父类字段和方法的机制。

java
// 父类(基类)
public class Animal {
    public String name;
    public int age;

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

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

// 子类(派生类)
public class Dog extends Animal {        // ① 用 extends 关键字
    public String breed;                  // ② 子类新增字段

    public void bark() {                  // ③ 子类新增方法
        System.out.println(name + " 在汪汪叫");
    }
}

Dog dog = new Dog();
dog.name = "旺财";
dog.age = 3;
dog.breed = "柴犬";
dog.eat();      // 来自父类
dog.sleep();    // 来自父类
dog.bark();     // 来自子类

二、继承的内存模型

子类对象包含父类的所有字段和方法,加上自己新增的部分。

三、方法重写(Override)

子类可以重写父类的方法,实现自己的行为:

java
public class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

public class Dog extends Animal {
    @Override                          // ① 注解,编译器检查
    public void makeSound() {
        System.out.println("汪汪汪");
    }
}

public class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("喵喵喵");
    }
}

重写规则

维度规则
方法签名必须相同
返回类型相同或协变(子类返回类型)
访问修饰符不能更严格
异常不能抛出更宽泛的检查异常
java
// ❌ 错误:方法签名不同(不是重写,是重载)
public void makeSound(String sound) { }

// ❌ 错误:访问修饰符更严格
@Override
private void makeSound() { }

// ✅ 正确
@Override
public void makeSound() { }

四、super 关键字

4.1 调用父类方法

java
public class Dog extends Animal {
    @Override
    public void makeSound() {
        super.makeSound();        // ① 调用父类方法
        System.out.println("汪汪汪");
    }
}

4.2 调用父类构造器

java
public class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
    }
}

public class Dog extends Animal {
    public String breed;
    public Dog(String name, String breed) {
        super(name);               // ① 必须放在第一行
        this.breed = breed;
    }
}

4.3 访问父类字段

java
public class Dog extends Animal {
    @Override
    public void printInfo() {
        System.out.println("name: " + super.name);  // 父类的 name
    }
}

五、构造器链

子类构造器必须先调用父类构造器

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

public class Dog extends Animal {
    public Dog() {
        super();    // ① 编译器自动添加(可以不写)
        System.out.println("Dog 无参构造器");
    }
}

// 输出:
// Animal 无参构造器
// Dog 无参构造器

六、继承的限制

6.1 单继承

Java 只支持单继承(一个类只能有一个直接父类):

java
// ❌ 编译报错
public class Dog extends Animal, Pet { }

// ✅ 多层继承
public class A { }
public class B extends A { }
public class C extends B { }  // C 间接继承 A

需要"多继承"效果时,用接口(第 21 章)或组合

6.2 访问父类的私有字段

java
public class Animal {
    private String secret = "秘密";   // private
}

public class Dog extends Animal {
    public void test() {
        System.out.println(secret);     // ❌ 编译报错:不可见
        System.out.println(super.secret); // ❌ 也不行
    }
}

父类的 private 字段,子类不能直接访问,需要通过 protected 或 getter/setter。

6.3 final 类不可继承

java
public final class String { ... }    // ❌ 不能 extends String

StringMath 等都是 final 类。

七、什么时候用继承?

关系推荐
Dog is Animal继承
Car has Engine组合(has-a)
Bird can Fly接口(can-do)

企业级原则:优先用组合而非继承(Effective Java Item 18)。

八、完整代码示例

java
/**
 * 继承综合示例
 *
 * 配套文档:第 17 章 继承
 */

// ============ 父类 ============
class Animal {
    protected String name;       // protected:子类可访问
    protected int age;

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

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

    public void makeSound() {
        System.out.println(name + " 发出声音");
    }
}

// ============ 子类 ============
class Dog extends Animal {
    private String breed;

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

    @Override                                    // 重写父类方法
    public void makeSound() {
        super.makeSound();                       // 调用父类
        System.out.println(name + " 汪汪叫");
    }

    public void fetch() {                        // 子类新增方法
        System.out.println(name + " 在捡球");
    }
}

// ============ 测试 ============
public class InheritanceDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("旺财", 3, "柴犬");

        // 来自父类的方法
        dog.eat();           // 旺财 在吃东西

        // 重写后的方法
        dog.makeSound();     // 旺财 发出声音\n旺财 汪汪叫

        // 子类自己的方法
        dog.fetch();         // 旺财 在捡球

        // 访问父类 protected 字段
        System.out.println("名字: " + dog.name);
        System.out.println("年龄: " + dog.age);
        System.out.println("品种: " + dog.breed);
    }
}

输出

Animal 构造器: 旺财
Dog 构造器: 柴犬
旺财 在吃东西
旺财 发出声音
旺财 汪汪叫
旺财 在捡球
名字: 旺财
年龄: 3
品种: 柴犬

九、本章小结

要点关键
继承本质子类复用父类字段和方法
extends单继承,子类只能有一个直接父类
方法重写子类重新实现父类方法,加 @Override 注解
super调用父类方法 / 构造器 / 字段
构造器链子类构造器必须先调用父类构造器
继承 vs 组合is-a 用继承,has-a 用组合

动手练习

练习 1:基础题

设计 Employee → Manager 继承关系:

java
Employee: name, salary, work()
Manager: bonus, manage()(重写 work())

练习 2:进阶题

实现一个图形继承体系:

java
Shape (abstract): area(), perimeter()
  ├── Circle (radius)
  ├── Rectangle (width, height)
  └── Triangle (a, b, c)

下一章第 18 章:多态

本站基于 VitePress 构建 · 由 Codebook 团队维护