接口定义能力契约、支持多实现与松耦合,抽象类表达类型归属、提供状态共享与代码复用;接口无构造器和字段,抽象类可含构造器、字段及具体方法;选接口重扩展性,选抽象类重继承复用。

接口和抽象类在 Java 中都用于抽象,但它们不是“换种写法”,而是解决不同问题的两种机制。关键不在“能不能做某事”,而在“为什么这样设计”。
语法层面:能写什么、怎么写
接口里不能有构造器,所有字段自动是 public static final,所有方法默认是 public abstract(Java 8+ 可加 default 或 static 方法)。它不保存状态,也不参与对象初始化。
抽象类可以有构造器、普通字段(private、protected 都行)、具体方法、抽象方法、静态方法,甚至初始化块。它是一个“半成品类”,能承载数据和逻辑。
- 接口用 implements 实现,一个类可实现多个接口
- 抽象类用 extends 继承,一个类只能继承一个抽象类
- 接口之间可用 extends 多继承(如
interface A extends B, C),抽象类不能多继承父类
语义层面:“能做什么” vs “是什么”
接口表达的是能力——比如 Flyable、Runnable、Serializable。它不关心你是鸟、飞机还是无人机,只约定“你得会飞”或“你得能运行”。这是一种 like-a 关系。
抽象类表达的是类型归属——比如 Animal、Shape、Vehicle。它描述了一组有共同属性(如 name、color)和部分共通行为(如 sleep()、draw())的类。这是一种 is-a 关系。
- 接口回答:“这个对象能响应哪些操作?”
- 抽象类回答:“这个对象属于哪一类?它天然拥有哪些特征和基础行为?”
架构设计意图:解耦 vs 复用
接口的核心价值是定义契约、支持松耦合。模块之间通过接口通信,彼此无需知道对方具体类型,便于替换、测试和扩展。例如,日志模块依赖 Logger 接口,不管背后是 FileLogger 还是 CloudLogger。
抽象类的核心价值是共享实现、减少重复代码。当多个子类有大量相同字段、构造逻辑或模板方法时,抽象类提供统一基底。例如,所有 HTTPServlet 子类都复用请求解析、生命周期钩子等通用逻辑。
- 选接口:当你需要让不相关的类具备同一行为,或强调可插拔性
- 选抽象类:当你有一组强关联的类,且它们共享状态、构造过程或核心算法骨架
- 常见组合:抽象类实现某个接口(如
abstract class AbstractList implements List),兼顾复用与契约
实际选型判断线索
遇到设计抉择时,可以快速自问几个问题:
- 是否需要定义字段或构造器?→ 必须用抽象类
- 是否希望一个类同时具备多种独立能力(如“可序列化 + 可比较 + 可关闭”)?→ 用多个接口
- 是否已有明确的类层级关系,且子类高度相似?→ 抽象类更自然
- 是否面向外部扩展(如插件、SPI)?→ 优先接口,降低使用者负担










