抽象类与接口在多态中应协同配合:抽象类封装共性逻辑与状态,接口定义行为契约;子类继承抽象类并实现接口,兼顾复用性与扩展性,多态调用面向接口、运行依赖具体子类。

抽象类和接口在多态中不是非此即彼的选择,而是可以协同配合、各司其职的组合工具。关键在于:用抽象类封装共性逻辑与状态,用接口定义行为契约,再通过继承+实现的方式让子类同时获得复用性和扩展性。
抽象类提供可复用的骨架,接口定义统一行为标准
当一组类有共同字段、初始化逻辑或部分通用方法(比如日志记录、资源校验、默认配置),适合用抽象类组织;而它们对外暴露的交互能力(如run()、cancel()、serialize())则应抽成接口——这样不同模块只依赖接口,不耦合具体实现细节。
- 抽象类负责“怎么做一部分”,比如
AbstractTask含id、createTime、validate()具体实现 - 接口负责“能做什么”,比如
Runnable、Cancellable、Serializable,多个接口可叠加实现 - 子类只需继承抽象类 + 实现所需接口,无需重复写基础字段和校验逻辑
接口作为多态入口,抽象类作为运行时载体
多态调用通常面向接口声明变量,但实际对象由继承抽象类的具体子类构造。这种分层让编译期类型安全、运行期灵活替换。
- 声明:
Runnable task = new DatabaseBackupTask();—— 编译看Runnable接口 - 运行:
DatabaseBackupTask继承AbstractBackupTask,复用连接池管理、重试机制等 - 后续可新增
FileBackupTask,同样继承AbstractBackupTask、实现Runnable,无缝接入原有调度系统
利用 default 方法桥接抽象类能力到接口层
JDK 8+ 的接口 default 方法,可把原本只能放在抽象类里的轻量通用逻辑,下沉到接口中,降低子类负担。
立即学习“Java免费学习笔记(深入)”;
- 例如在
Loggable接口中定义:default void logStart() { System.out.println("[" + getClass().getSimpleName() + "] started"); } - 所有实现该接口的类(无论是否继承某抽象类)都能直接调用,无需重复写
- 若某子类需定制日志格式,可重写该 default 方法,不破坏接口契约
避免常见误用:不为继承而继承,不为接口而接口
协同的前提是职责清晰。强行让抽象类实现某个接口,或让接口继承另一个接口,若无明确语义支撑,反而增加理解成本。
- 抽象类实现接口,仅当它天然具备该行为且能提供合理默认实现(如
AbstractList实现List) - 接口之间继承,适用于“is-a”关系明确的场景(如
QueueextendsCollection),而非为了凑功能 - 一个类同时 extends 抽象类 + implements 多个接口,是推荐模式;但若抽象类已包含大量业务方法,接口只剩空壳,说明抽象层级失衡


















