创建日期:2026-09-08 | 最近更新:2026-09-08 主线 JDK 11(本机实测可跑);record(JDK 16+)标注不实测。
Java 复习手册 3:继承、接口与多态
上一篇会「造单个类」了。这篇解决「类与类之间怎么关联」——继承(复用代码)、接口(定义能力)、多态(同一调用,不同表现)。这是 Java 面向对象真正的分水岭,也是你以前「学了又忘」的重灾区,放慢点。
1. 继承:子类复用父类,extends
// 父类
public class Animal {
protected String name; // protected:子类能访问
public Animal(String name) { this.name = name; }
public void speak() { System.out.println("..."); }
}
// 子类:extends 父类
public class Dog extends Animal {
public Dog(String name) { super(name); } // super(...) = 调父类构造器(必须是子类构造器第一句)
@Override // 覆写:重新实现父类方法
public void speak() {
System.out.println(name + ":汪汪");
}
}
Animal a = new Dog("旺财");
a.speak(); // 运行时调用的是 Dog 的覆写版 → 「旺财:汪汪」这就是多态
必须记住的三句话:
- Java 是单继承:一个类只能
extends一个父类(想要多种能力 → 用接口,见下); - 子类构造器第一行要
super(...)调父类构造器;父类有带参构造器而你没写 super → 编译报错; - 覆写加
@Override(编译器帮检查签名),父类方法想让子类能覆写就别final。
2. final:三个「锁死」用法(别和 static 混)
| 位置 | 作用 |
|---|---|
final class X | 这个类不能被继承(如 String) |
final int x = 1 | 变量只能赋值一次(常量) |
final 方法 | 子类不能覆写 |
结合上篇:
public static final double PI = 3.14;= 「属于类的只读常量」。
3. 抽象类:只定规矩,不做实现
当父类「有公共字段和部分方法,但某个方法说不清怎么实现」时,把它声明成抽象:
public abstract class Shape {
protected String color;
public Shape(String color) { this.color = color; }
public abstract double area(); // 只有声明,没有方法体,交给子类实现
}
public class Circle extends Shape {
private double r;
public Circle(String color, double r) { super(color); this.r = r; }
@Override
public double area() { return Math.PI * r * r; } // 子类必须实现抽象方法
}
abstract类不能new(没实现完整),只能被继承;- 子类必须实现所有抽象方法,否则子类也得是 abstract;
- 适用:几个类是同一个物种(is-a),共享状态 + 有共同但不完整的行为。
4. 接口:只定义「会什么」,不管「是什么」
接口比抽象类更「纯」:只列方法签名,连字段都没有(常量除外),谁实现谁就得会这些能力。
public interface Flyable {
void fly(); // 默认 public abstract,方法体没有
default void log() { // default 方法:接口里也能给默认实现(可选)
System.out.println("飞行动作已记录");
}
}
public class Bird extends Animal implements Flyable { // 先 extends,再 implements,可多个
public Bird(String name) { super(name); }
@Override public void speak() { System.out.println("啾啾"); }
@Override public void fly() { System.out.println(name + " 飞起来了"); }
}
抽象类 vs 接口(高频面试 + 高频遗忘):
| 抽象类 | 接口 | |
|---|---|---|
| 关键词 | abstract class | interface |
| 继承数量 | 只能 extends 一个 | 可以 implements 多个 |
| 字段 | 可以有实例字段 | 基本只能常量(JDK9+ 可私有方法) |
| 定位 | 「是一种」(is-a),共享状态 | 「能做某事」(has ability) |
| 选谁 | 共享代码/状态 → 抽象类 | 定义能力契约 → 接口(现代 Java 更偏爱接口) |
5. 多态:把对象当父类型/接口用
多态 = 同一个引用类型,实际指向不同子类对象,调用同名方法走各自实现:
Animal[] animals = { new Dog("旺财"), new Bird("小雀") };
for (Animal a : animals) {
a.speak(); // 各叫各的 → 汪 / 啾啾
if (a instanceof Bird b) { // instanceof 判断真实类型;JDK16+ 可这样绑定变量
b.fly();
}
}
- 向上转型:
Animal a = new Dog(...)—— 子类当父类用,天然允许; - 声明成
Animal/Flyable,代码只依赖「抽象」,加新子类不用改调用方 → 这就是面向接口编程的意义; instanceof判断后要强转才能用子类特有方法(((Dog) a).watchDoor())。
6. 每个类都该会覆写的三个方法
@Override
public String toString() { // 打印对象时的文字,默认是 类名@地址
return "Dog(" + name + ")";
}
@Override
public boolean equals(Object o) { // 定义「两个对象什么时候相等」
if (this == o) return true;
if (!(o instanceof Dog that)) return false;
return this.name.equals(that.name);
}
@Override
public int hashCode() { // 跟 equals 配套(放进 HashSet/HashMap 必需)
return name.hashCode();
}
口诀:改 equals 必改 hashCode,否则对象放进 Set/Map 会「看着相等却找不着」。
想少写样板?现代 Java 用 record(JDK 16+):
record Point(int x, int y) {}自动给你构造器 +equals/hashCode/toString——等你会搭项目后再回来享受它。
动手 3 分钟
建 Shape(abstract) + Circle/Rectangle 两个子类,写个方法打印面积总和;再给 Dog 覆写 toString 看效果。
自测
- 单继承指什么?怎么做到「同时是 A 又能飞」?
- 覆写和重载的区别?
- 抽象类能不能 new?为什么?
- 接口里的方法默认是什么修饰符?
equals和hashCode为什么要一起改?