接口定义“能做什么”,专注行为契约与多实现;抽象类描述“是什么”,强调类的共性结构与继承关系;二者应分层协作,接口对外暴露能力,抽象类对内简化实现。

接口和抽象类在设计时明确职责边界,关键在于先回答一个问题:你是在定义“能做什么”,还是在描述“是什么”。前者用接口,后者用抽象类。选对工具,边界自然清晰。
接口专注定义行为契约
接口只说“能干什么”,不涉及状态、构造逻辑或共用实现。它代表一种可插拔的能力,比如PaymentProcessor、CacheService或NotificationSender。
- 每个接口只聚焦一类能力,方法数量精简(通常 2–5 个),名称体现意图,如send()、put()、validate()
- 避免把日志、缓存、配置加载塞进同一个SystemService——这不是契约,是包袱
- 允许一个类实现多个接口,比如User同时实现Serializable、Comparable<User>和Auditable,能力正交、组合自由
抽象类用于表达共性结构
抽象类回答“它属于哪一类”,强调血缘关系和共享基础。比如Vehicle、ReportGenerator或BaseHttpClient,它们有共同字段、初始化流程或模板逻辑。
- 可以含构造方法、protected 字段、普通方法、静态方法,甚至部分已实现的业务逻辑
- 子类必须继承它(单继承限制),且需实现所有抽象方法;若不想实现,自己也得声明为 abstract
- 适合封装重复流程,例如generateReport()调用readData()(抽象)→formatData()(抽象)→export()(已实现)
组合使用:接口定契约,抽象类减重复
现代 Java 中二者不是二选一,而是分层协作。接口对外暴露能力入口,抽象类对内简化实现成本。
立即学习“Java免费学习笔记(深入)”;
- 对外依赖永远声明为接口类型:private final PaymentProcessor processor;,而不是AlipayProcessor
- 多个实现类共用加密、重试、日志前缀等逻辑?提取为AbstractCreditCardProcessor,让它 implements PaymentProcessor
- 接口中 default 方法仅限无状态辅助逻辑(如isEmpty()校验),复杂流程、依赖注入、构造参数仍归抽象类管
警惕边界模糊的信号
当设计开始让人犹豫“该用 interface 还是 abstract class”,往往说明职责本身已经混杂,需要回退一步重新建模。
- 接口里出现大量空方法实现?说明它太胖,应按功能拆成Readable、Writable、Searchable
- 抽象类里塞了十几个 final 方法,子类几乎无法扩展?说明它正在变成“伪接口”,该转成接口+默认实现
- 某个类既要 extends 又要 implements 很多东西,且父类字段和接口方法语义不搭?大概率是抽象类承担了本该由接口表达的能力角色


















