跳转到主要内容
模块 3 · 第 2 课(共 2)课程中的6/14~15 min
模块课程(2/2)

多态性和抽象类

多态性和抽象类是高级的面向对象编程概念,使我们能够编写独立于具体实现的灵活、可扩展的代码。

多态性

术语“多态性”(多种形式)是指将不同类的对象视为属于同一类型的能力。在Java中,超类引用变量可以存储对其任何子类的对象的引用。

Java
class Animal {
    public void makeSound() {
        System.out.println("Verso...");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("Woof");
    }
}

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

由于多态性,我们可以这样做:

Java
Animal myAnimal1 = new Dog(); // Polymorphism
Animal myAnimal2 = new Cat(); // Polymorphism

myAnimal1.makeSound(); // Executes Dog's method (Woof)
myAnimal2.makeSound(); // Executes Cat's method (Miao)

调用哪个方法的决定是在运行时(后期绑定或动态分派)根据实际对象而不是引用变量的类型做出的。

抽象类 (abstract)

抽象类是用 abstract 关键字标记的类,不能直接实例化(不能执行 new MyAbstractClass())。它充当其他类的“部分模板”。

抽象方法

抽象类可以包含抽象方法:声明的方法没有主体(没有花括号,没有代码),以分号结尾。非抽象子类需要实现所有继承的抽象方法。

Java
abstract class Shape {
    String color;

    // Abstract method (no body)
    public abstract double getArea();
}

class Circle extends Shape {
    double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // Required to implement getArea
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

动态绑定/后期绑定

Java如何知道运行时要执行哪个方法?此过程称为后期绑定(或动态绑定)。与重载(在编译时解决)不同,编译器不知道将执行哪个特定方法。相反,它生成通用调用指令。在运行时,Java 虚拟机 (JVM) 检查内存中的实际对象并调用该对象的类中定义的方法实现。

抽象类和缺失的实现

如果子类继承自抽象类,它有两种选择:

  1. 实现所有抽象方法:在这种情况下,子类可以是普通(具体)类,并且可以实例化。
  2. 声明自身为抽象:如果子类没有为所有继承的抽象方法提供实现,也必须使用abstract关键字进行声明。
Java
abstract class Animal {
    public abstract void makeSound();
}

// This class MUST be abstract because it does not implement makeSound()
abstract class Canine extends Animal {
    // Inherits makeSound() but does not implement it
}

自己尝试一下

锻炼#java.m3.l2.e1
尝试:0加载中...

声明一个 Shape 类型的变量 s 并使用多态性为其分配一个新的 Circle 对象。

正在加载编辑器...
显示提示

编写“Shape s = new Circle();”以使用基本类型作为引用类型。

3 次尝试后可用的解决方案

锻炼#java.m3.l2.e2
尝试:0加载中...

使 Shape 类抽象并添加抽象方法 double getArea()。然后完成 Square,使其扩展 Shape 并实现 getArea() 返回 side * side。

正在加载编辑器...
显示提示

在 Shape 中声明 `public abstract double getArea();`。在 Square 中,添加 `@Override public double getArea() { return side * side; }`。

3 次尝试后可用的解决方案

锻炼#java.m3.l2.e3
尝试:0加载中...

在 main 方法中声明一个 Shape[] 类型的数组,其中包含一个半径为 2.0 的 Circle 对象和一个边长为 3.0 的 Square 对象。然后,使用循环(for 或 for-each)通过调用 getArea() 方法来打印每个形状的面积。

正在加载编辑器...
显示提示

使用“Shape[] Shapes = { new Circle(2.0), new Square(3.0) };”声明数组,并使用“for (Shape s : Shapes)”打印“s.getArea()”循环遍历它。

3 次尝试后可用的解决方案