主题
Chapter 11 · 抽象类与接口
"我只告诉你做什么,怎么做你自己想办法" — 抽象的艺术。
🎯 本章目标
- 理解 抽象类(abstract class)的用途
- 理解 接口(interface)及其演进(JDK 8 / 9 新特性)
- 掌握 抽象类 vs 接口 的区别
- 学会 多接口实现 模拟"多继承"
- 理解 面向接口编程 思想
1. 抽象的需求
回到上一章的动物例子:
java
class Animal {
void sound() { System.out.println("某种声音"); } // 这个默认实现毫无意义
}Animal.sound() 写啥都不合适:
- 写"某种声音" → 调用方一脸懵
- 不写 → 不能强制子类实现
解决:让 Animal 变成抽象类,sound() 变成抽象方法:
java
abstract class Animal {
abstract void sound(); // 只声明,不实现
void eat() { ... } // 普通方法可以有实现
}💡 抽象类不能 new,但可以被继承。子类必须实现所有抽象方法。
2. 抽象类详解
java
abstract class Animal {
String name;
Animal(String name) { this.name = name; } // ✅ 可以有构造器
abstract void sound(); // ✅ 抽象方法(无方法体)
void eat() { // ✅ 可以有具体方法
System.out.println(name + " 在吃饭");
}
}
class Dog extends Animal {
Dog(String name) { super(name); }
@Override
void sound() { System.out.println("汪汪"); } // 必须实现
}抽象类的特点
| 特性 | 说明 |
|---|---|
| 关键字 | abstract class |
| 实例化 | ❌ 不能 new(直接),但可被继承后实例化 |
| 抽象方法 | abstract void m();(无方法体) |
| 普通方法 | ✅ 可以有 |
| 字段 | ✅ 可以有任意字段 |
| 构造器 | ✅ 可以有(被子类调用) |
| 继承 | 子类必须实现所有抽象方法(除非子类也是抽象的) |
3. 接口:纯粹的"契约"
接口比抽象类更纯粹:只规定能做什么,完全不管怎么做。
java
interface Flyable {
void fly();
}
class Bird implements Flyable {
@Override
public void fly() { System.out.println("鸟在飞"); }
}
class Plane implements Flyable {
@Override
public void fly() { System.out.println("飞机在飞"); }
}接口的演进
| Java 版本 | 接口允许的内容 |
|---|---|
| JDK 7 及以前 | 只能有抽象方法、public static final 常量 |
| JDK 8 | + default 方法(默认实现)、static 方法 |
| JDK 9 | + private 方法(接口内部复用) |
JDK 8+ 接口完整能力
java
interface Vehicle {
int MAX_SPEED = 200; // public static final(默认)
void start(); // public abstract(默认)
default void honk() { // 默认方法(JDK 8)
System.out.println("嘟嘟!");
}
static Vehicle create(String type) { // 静态方法(JDK 8)
return type.equals("car") ? new Car() : new Bike();
}
private void log(String msg) { // 私有方法(JDK 9)
System.out.println("[Vehicle] " + msg);
}
}💡 default 方法的意义:给老接口加新方法,已有实现类不需要改!这就是 Java 8 引入的关键原因(为了 Stream API 给 Collection 加方法)。
4. 多接口实现
Java 不支持多继承(一个类只能 extends 一个父类),但可以实现多个接口:
java
interface Swimmable { void swim(); }
interface Flyable { void fly(); }
interface Runnable { void run(); }
class Duck implements Swimmable, Flyable, Runnable {
public void swim() { System.out.println("鸭子游泳"); }
public void fly() { System.out.println("鸭子飞"); }
public void run() { System.out.println("鸭子走路"); }
}🦆 鸭子真的能游、能飞、能跑 —— 通过多接口模拟"多继承"。
5. 抽象类 vs 接口(核心对比)
| 维度 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 字段 | 任意类型字段 | 只能 public static final 常量 |
| 方法 | 任意方法(含抽象、具体) | 抽象 + default + static + private |
| 构造器 | ✅ 有 | ❌ 无 |
| 继承 | 单继承(extends 1 个) | 多实现(implements 多个) |
| 关系 | "是一个"(is-a) | "能做什么"(can-do) |
| 状态 | 可以有状态(字段) | 通常无状态 |
怎么选?
| 场景 | 选择 |
|---|---|
| 多个类共享代码(不仅是规范) | 抽象类 |
| 多个类共享行为约定(仅规范) | 接口 |
| 需要构造器、字段 | 抽象类 |
| 需要"伪多继承" | 接口 |
| Java 8+ 给老代码加新功能 | 接口 default |
💡 现代 Java 倾向:优先用接口,必要时再用抽象类辅助。
6. 面向接口编程(核心思想!)
java
// ❌ 紧耦合
class OrderService {
AliPay payment = new AliPay(); // 写死支付宝
void checkout() { payment.pay(100); }
}
// ✅ 面向接口
interface Payment { void pay(double amount); }
class OrderService {
Payment payment; // 依赖接口,不依赖实现
OrderService(Payment p) { this.payment = p; }
void checkout() { payment.pay(100); }
}
new OrderService(new AliPay()).checkout();
new OrderService(new WeChatPay()).checkout();
new OrderService(new BankPay()).checkout();好处:
- 想换支付方式?换实现类即可
- 单元测试?传一个 mock Payment
- 这就是 Spring 框架的"依赖注入"哲学
7. 实战练习
| 文件 | 内容 |
|---|---|
AbstractClassDemo.java | 抽象类基础 |
InterfaceDemo.java | 接口基础与 default 方法 |
MultiInterface.java | 多接口实现 |
PaymentSystem.java | 面向接口编程实战 |
8. 浏览器演示
打开 demo.html:
- 抽象类 / 接口能力对照表
- 接口 default 方法演示
- 多接口实现可视化
9. 面试可能会问什么?
Q1: 抽象类和接口有什么区别?
| 维度 | abstract class | interface |
|---|---|---|
| 设计意图 | "是一个"父子血缘 | "能做什么"行为约定 |
| 字段 | 任意 | public static final |
| 方法 | 任意修饰符 | 默认 public |
| 构造器 | 有 | 无 |
| 继承 | 单继承 | 多实现 |
Q2: 为什么 Java 接口 JDK 8 引入 default 方法?
答:保持向后兼容。Java 8 给 Collection 接口加了 stream() 方法,如果没有 default,所有已实现 Collection 的代码都会编译失败。
Q3: 接口能多继承吗?
答:可以!接口 可以 extends 多个接口(注意是 extends 不是 implements):
java
interface A {}
interface B {}
interface C extends A, B {} // ✅ 接口可以多继承类不能多继承类,但可以实现多个接口:
java
class X extends Y implements A, B, C {} // 限实现接口可多个Q4: 接口的方法默认是什么修饰符?
- 抽象方法:
public abstract(编译器自动加) - default 方法:
public - 字段:
public static final
Q5: 什么时候用抽象类,什么时候用接口?
- 共享 状态 + 代码 → 抽象类
- 仅共享 行为约定 → 接口
- 需要 "伪多继承" → 接口
- 实在不知道?先用接口
Q6: 一个类能既继承抽象类又实现接口吗?
答:可以。先 extends 后 implements:
java
abstract class Animal { ... }
interface Flyable { void fly(); }
class Bird extends Animal implements Flyable { ... }🎁 本章小结
✅ 抽象类:可有可无具体实现,子类必须实现抽象方法
✅ 接口:行为契约,JDK 8+ 可以有 default / static 方法
✅ 类只能单继承,但可实现多个接口(伪多继承)
✅ 抽象类表"is-a",接口表"can-do"
✅ 面向接口编程是松耦合的关键🔗 导航
- ⬅️ 上一章:Chapter 10 · 多态
- ➡️ 下一章:Chapter 12 · 常用类