主题
  • 默认模式
  • 浅蓝色模式
  • 淡绿色模式
  • 深夜模式

什么是继承?

继承是 OOP(面向对象编程)的一个核心特性。它是指从一个已有的类(称为父类或基类)派生出新的类(称为子类或派生类)。子类会继承父类的数据属性和行为方法,并可以在此基础上扩展新的能力。

基类一旦被继承,就构成了代码架构的基础,修改会就会产生连锁反应。因此,在设计时务必全面考量,力求避免对基类的后续改动。

本文将系统地介绍 Java 继承的使用理由、is-a 关系、Protected 关键字,方法重写(Override)、Super 关键字、继承的优缺点并提供相应的示例代码以帮助理解。

Java 继承结构图


is-a 关系

继承用于表示类之间的严格is-a关系,只有在两个类确实符合这种关系时,才应使用继承。

✅ 例如,以下情况适合使用继承:

  • 橙子是一种水果
  • 开创者是一个网站
  • 外科医生也是医生

✅ 而以下情况则不适合使用继承,因为它们描述的是 has-a(拥有)关系,而非is-a关系:

  • 汽车拥有发动机
  • 房子拥有窗户

✅ 以下是使用继承关系实现的实例:

java
复制
class Animal {
    public void eat() {
        System.out.println("I can eat food.");
    }

    public void sleep() {
        System.out.println("I can sleep.");
    }
}

class Cat extends Animal {
    public void catchMice() {
        System.out.println("I can catch mice.");
    }
}

class Main {
    public static void main(String[] args) {

    Cat tabbyCat = new Cat();
    tabbyCat.eat();
    tabbyCat.sleep();
    tabbyCat.catchMice();
}

执行上面的程序后,运行结果:

I can eat food.
I can sleep.
I can catch mice.

在上面的代码中,我们从父类 Animal 继承了 Cat 子类。Cat 类从 Animal 类继承了eat()sleep()方法。


Protected 关键字

✅ 在前面的章节中,我们已经初步了解了在 Java 中访问控制修饰符基本概念与作用:

  • private:仅允许在定义该成员的类内部访问。该修饰符提供了最严格的封装保护,同时也限制了代码的可重用性和扩展性。
  • public:可以被任何其他类直接访问。虽然提供了最高的灵活性,但过度使用会破坏封装性。
  • protected:为继承体系设计,允许同一包及所有子类访问,平衡封装性与扩展性。

您可以将方法和字段设置为 protected。受保护的成员可以在定义它的类、同一包中的其他类以及任何子类中被访问。

✅ 下面是一个清晰的 Java 访问修饰符作用域的对照表:

修饰符 本类 同包 子类 全局
public 可访问 可访问 可访问 可访问
private 可访问 不可访问 不可访问 不可访问
protected 可访问 可访问 可访问 不可访问

方法重写(Override)

在继承机制中,子类可以复用父类的方法,省去了重复编写代码的麻烦,大大提升了代码的复用效率。不过,并不是每一个从父类继承而来的方法,都完全契合子类实际的业务场景。

这时候,我们就需要对这些方法进行重写——既保留原有逻辑的精华,又融入子类自己的特色。通过这种方式,子类既延续了父类的能力,又拥有了改变和拓展的自由度,在保持一致接口的同时,实现属于自己的行为逻辑。

✅ 下面是一个方法重写的示例:

java
复制
class Animal {
    protected String type = "animal";

    public void eat() {
        System.out.println("I can eat food.");
    }

    public void sleep() {
        System.out.println("I can sleep.");
    }
}

class Cat extends Animal {

    @Override
    public void eat() {
        System.out.println("I love eating cat food.");
    }

    public void catchMice() {
        System.out.println("I can catch mice.");
    }
}

class Main {
    public static void main(String[] args) {

    Cat tabbyCat = new Cat();
    tabbyCat.eat();
    tabbyCat.sleep();
    tabbyCat.catchMice();
}

执行上面的程序后,运行结果:

I love eating cat food.
I can sleep.
I can catch mice.

在这里,eat() 方法同时存在于超类Animal和子类Cat中。通过重写eat()方法,子类Cat可以根据自身的需求提供特定的实现,从而覆盖从超类继承而来的默认行为。这种机制使得子类能够在不改变方法签名的情况下,定制属于自己的功能逻辑,充分体现了面向对象编程中Java 多态的特性。

在上面的示例中,我们使用了@Override注解来明确告诉编译器当前方法正在重写父类的方法。虽然这个注解不是强制要求的,但使用它是一个良好的编程习惯。在接下来的教程中,我们将深入探讨方法重写的细节和使用场景。


Super 关键字

super 关键字在 Java 中主要用于在子类中访问和调用父类的成员(包括方法、构造方法和属性)。

✅ 其主要用途体现在以下几个方面:

  • 调用父类构造方法:必须是子类构造方法中的第一条语句。
  • 调用父类方法:用于调用在子类中被重写(Override) 的父类方法。
  • 访问父类属性:用于访问在子类中被隐藏(Hide)(即同名)的父类字段。

1. 调用父类的构造方法

在子类的构造方法中,使用super()来调用父类的构造方法,必须放在子类构造方法的第一行。

java
复制
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }
}

class Cat extends Animal {
    int age;

    Cat(String name, int age) {
        super(name); // 调用父类构造方法
        this.age = age;
    }
}

2. 调用父类被重写的方法

当子类重写了父类的方法后,如果仍需调用父类的原始方法,可以使用 super.方法名()

java
复制
class Animal {
    public void eat() {
        System.out.println("Animal is eating.");
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        super.eat(); // 先调用父类的eat方法
        System.out.println("Cat is eating fish.");
    }
}

执行上面的程序后,运行结果:

Animal is eating.
Cat is eating fish.

3. 访问父类的属性(不常用)

当子类和父类有同名字段时,可以使用super.字段名来明确访问父类的属性(但通常建议避免字段重名)。

java
复制
class Animal {
    String sound = "Animal sound";
}

class Cat extends Animal {
    String sound = "Meow";

    void printSounds() {
        System.out.println(super.sound); // 访问父类的sound
        System.out.println(this.sound);  // 访问子类的sound
    }
}

执行上面的程序后,运行结果:

Animal is eating.
Cat is eating fish.

注意:在调用构造函数与使用super方法时存在一些关键区别。要了解更多信息,请访问 Java super 关键字


继承的优缺点

1. 继承的优点

  • 子类可以继承父类的字段和方法,无需重新编写相同的代码。这大大减少了代码冗余,提高了开发效率。
  • 子类可以根据自身需要,重写(Override)父类的方法,提供特定于自己的实现。还允许 “一个接口,多种实现”。
  • 继承允许我们创建清晰、逻辑分明的类层次结构,使代码模型更易于理解和维护。
  • 父类引用可以指向子类对象。这意味着你可以编写更通用、更灵活的代码。

2. 继承的缺点

  • 不支持多重继承(一个类不能同时继承多个父类)。
  • 父类实现的任何改变(如修改方法签名、字段名)都可能 “破坏” 子类的功能。
  • 过深的继承层次(例如:A -> B -> C -> D -> E)会使代码难以理解和调试。
  • 继承会暴露父类的实现细节给子类(通过protected成员),这就违反了封装原则,是一种糟糕的设计。
  • 因为继承会导致代码异常混乱, 99.99% 的人都用不好继承,继承都逐渐被替代为接口。

因此,在使用继承时需要权衡其优缺点,并根据具体问题选择合适地解决方案。



评论区 0
发表评论