← Java 学习路线(共 8 章)

第 5 章 继承、抽象类与接口

red wenzi · 2026-09-15 · 编程语言 · Java · 📖 预计阅读 15 分钟 · 共 2 道练习
🎯 本章你会学到:extends / implements、@Override 与运行时多态:同一段代码处理不同对象。建议边读边敲,每节练习先自己做,再展开答案对照。

多态解决的问题是:**同一段代码,处理不同的具体类型**。在 Java 里这件事比 C++ 更省心——方法默认就是动态绑定的,不需要记得写 virtual

接口 + record:最简多态

接口与多态
import java.util.ArrayList;
import java.util.List;

public class Main {
    interface Shape {                 // 接口:只有约定,没有状态
        double area();
        String name();
    }

    record Circle(double r) implements Shape {   // record:一行写完不可变数据类
        public double area() { return Math.PI * r * r; }
        public String name() { return "circle"; }
    }

    record Square(double side) implements Shape {
        public double area() { return side * side; }
        public String name() { return "square"; }
    }

    public static void main(String[] args) {
        List<Shape> shapes = new ArrayList<>();
        shapes.add(new Circle(1));
        shapes.add(new Square(2));

        for (Shape s : shapes) {       // 这里不关心具体是圆还是方
            System.out.printf("%s %.2f%n", s.name(), s.area());
        }
    }
}
运行结果
circle 3.14
square 4.00

抽象类:需要共享状态或部分实现时

抽象类与方法重写
public class Main {
    static abstract class Animal {
        private final String name;

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

        abstract String sound();                      // 子类必须实现

        String describe() {                           // 共有逻辑写在父类
            return name + " says " + sound();         // 运行期按实际类型调用
        }
    }

    static class Dog extends Animal {
        Dog(String name) { super(name); }
        String sound() { return "woof"; }
    }

    static class Cat extends Animal {
        Cat(String name) { super(name); }
        String sound() { return "meow"; }
    }

    public static void main(String[] args) {
        Animal[] zoo = { new Dog("Wangcai"), new Cat("Mimi") };
        for (Animal a : zoo) {
            System.out.println(a.describe());
        }
    }
}
运行结果
Wangcai says woof
Mimi says meow
概念作用什么时候用
extends继承父类(单继承)确实存在“是一种”关系,且要复用实现
implements实现接口(可多个)只约定能力,最推荐的方式
abstract抽象类 / 抽象方法需要共享字段或部分实现时
@Override声明这是重写强烈建议都写,编译器帮你查签名
record不可变数据类只装数据、不需要继承时
⚠️ 易错点 父类构造器要用 super(...) 显式调用(不写时编译器会插入无参 super(),父类没有无参构造器就报错)。
⚠️ 易错点 接口里的方法默认是 public,实现时不能漏写 public,否则编译报“试图分配更低的访问权限”。

✍️ 本节练习

5.1必做抽象类与两个子类

定义抽象类 Shape(抽象方法 area()),派生 CircleRect。读入半径、宽、高,输出两个面积(2 位小数)。

输入 一行三个实数:半径 宽 高。

输出 两行:circle=rect=(2 位小数)。

样例输入
1 2 3
样例输出
circle=3.14
rect=6.00

💡 提示 Shape[] shapes = { new Circle(r), new Rect(w, h) }; 然后循环调用 area()

✅ 查看参考答案与解析
import java.util.Scanner;

public class Main {
    static abstract class Shape {
        abstract double area();
    }

    static class Circle extends Shape {
        private final double r;
        Circle(double r) { this.r = r; }
        double area() { return Math.PI * r * r; }
    }

    static class Rect extends Shape {
        private final double w, h;
        Rect(double w, double h) { this.w = w; this.h = h; }
        double area() { return w * h; }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        double r = sc.nextDouble();
        double w = sc.nextDouble();
        double h = sc.nextDouble();

        Shape[] shapes = { new Circle(r), new Rect(w, h) };
        System.out.printf("circle=%.2f%n", shapes[0].area());
        System.out.printf("rect=%.2f%n", shapes[1].area());
    }
}

解析 数组元素类型写成父类,实际对象是子类——调用 area() 时 JVM 按实际类型分派,这就是多态。

5.2挑战多形面积求和

定义接口 Shapearea()),实现圆与正方形。读入 n,随后 n 行每行是 C r(圆)或 S a(正方形),输出总面积(2 位小数)。

输入 第一行 n;接下来 n 行 C rS a

输出 一行 total=总面积(2 位小数)。

样例输入
3
C 1
S 2
C 2
样例输出
total=19.71

💡 提示 用 List<Shape> 收集对象,最后遍历求和;C/SString 比较。

✅ 查看参考答案与解析
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    interface Shape {
        double area();
    }

    record Circle(double r) implements Shape {
        public double area() { return Math.PI * r * r; }
    }

    record Square(double side) implements Shape {
        public double area() { return side * side; }
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        List<Shape> shapes = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            String kind = sc.next();
            double v = sc.nextDouble();
            shapes.add(kind.equals("C") ? new Circle(v) : new Square(v));
        }

        double total = 0;
        for (Shape s : shapes) {
            total += s.area();
        }
        System.out.printf("total=%.2f%n", total);
    }
}

解析 把所有对象装进 List<Shape> 再统一处理,是面向对象里最常用的组织方式;kind.equals("C") 而不是 kind == "C"

📚 本文概念都在知识大全:

JVM 与字节码 · JDK · String 与 StringBuilder · 集合框架 · 泛型 · 受检异常 · 继承与接口 · record · Stream API