选抽象类还是接口取决于关系本质与问题场景:抽象类表达is-a关系、复用结构,适合同源类共享字段和逻辑;接口表达can-do关系、定义契约,适合跨领域统一行为或多能力组合。

选抽象类还是接口,关键看你想表达什么关系、解决什么问题,而不是语法上能不能写出来。
看它“是什么”还是“能做什么”
抽象类表达 is-a 关系,强调身份归属和共性结构。比如 Animal 抽象类定义 name、age 和 sleep(),Dog 和 Cat 是它的子类——它们本质上都是动物。
接口表达 can-do 关系,只约定能力,不问出身。比如 Flyable 接口只要求实现 fly(),Bird、Drone、Superman 都能实现,彼此毫无继承联系。
- 多个类天然属于同一类事物,且共享字段或基础逻辑 → 优先抽象类
- 不同领域的类需要统一行为(如日志、序列化、回调)→ 优先接口
- 一个类需同时具备多种能力(如可序列化 + 可比较 + 可克隆)→ 必须用接口(Java 不支持多继承)
看你要复用“结构”还是定义“契约”
抽象类适合封装模板逻辑:初始化流程、资源清理、通用校验、默认状态等。它能带字段、构造器、protected 方法,子类直接复用。
接口适合定义对外契约:方法签名、行为语义、调用约定。它稳定、解耦、易替换,适合跨模块、跨团队协作。
- 需要控制子类构造方式(比如强制传入配置对象)→ 抽象类有构造器,接口没有
- 要让下游自由实现,又不想每次新增方法都改所有实现类 → 接口加 default 方法更安全
- 已有类层次较深,想插入一层共性抽象(如从 Animal 派生 Mammal)→ 抽象类更自然
看现代 Java 的协同模式
真实项目中,二者常配合使用:接口定边界,抽象类填骨架。
典型例子:List 是接口,定义 add/remove/get 等契约;AbstractList 是抽象类,基于 iterator() 实现了 indexOf()、contains() 等通用逻辑;ArrayList 和 LinkedList 各自继承 AbstractList,专注优化底层存储。
- 先定义接口暴露能力(对外稳定)
- 再提供抽象类封装通用实现(对内复用)
- 具体实现类只专注差异化逻辑(如线程安全、缓存策略、IO 优化)
看扩展与维护成本
接口一旦发布,修改代价高:加抽象方法会强制所有实现类改动;default 方法虽缓解,但不能替代设计初衷。
抽象类修改相对灵活:新增 protected 方法不影响子类;加字段或构造参数可通过重载兼容。
- 面向外部 SDK 或 API 提供方 → 接口为主,default 方法谨慎扩
- 内部框架或组件基类 → 抽象类更可控,便于统一管控生命周期
- 不确定未来是否要加状态或初始化逻辑 → 初期用接口,后续可补抽象类实现
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











