抽象类用于表达“是不是”的本质归属关系,如Animal与Dog的is-a关系,支持状态共享和构造控制;接口用于表达“能不能”的行为契约,如Runnable定义可执行能力,支持多实现和跨体系复用。

直接看核心逻辑:如果想表达“这个东西是不是某类事物”,用抽象类;如果想表达“这个东西能不能做某件事”,用接口。
“是不是”——用抽象类建模身份与继承关系
抽象类代表一种本质归属,强调子类和父类之间存在天然的、不可分割的类别关系。比如 Animal 是 Dog 和 Cat 的共同上层,它们“是”动物,不是“能”动物——这种血缘式继承只能靠抽象类承载。
- 子类必须属于该抽象类所定义的范畴(如 Mammal → Whale、Bat,它们都是哺乳动物)
- 需要共享可变状态(如 protected Logger logger、ConnectionPool pool)
- 需控制构造流程(如抽象类构造器中统一加载配置、初始化模板引擎)
- 存在固定执行骨架(如模板方法 pattern:final execute() { before(); doWork(); after(); })
“能不能”——用接口声明能力与行为契约
接口描述的是能力契约,不关心对象从哪来、属于谁,只关心它“能不能”响应某个动作。一个类可以同时是“能序列化”、“能比较”、“能克隆”的,这些能力彼此正交,互不干扰。
- 跨继承体系的能力复用(Runnable 被 Thread、TimerTask、CompletableFuture 实现)
- 一个类需具备多种独立职责(如 OrderService 同时实现 PaymentCapable、AuditLoggable、Retryable)
- 对外暴露稳定 API(如 List 接口长期不变,ArrayList、LinkedList 自由替换)
- 便于 mock 和解耦(依赖接口而非具体类,单元测试更轻量)
现代 Java 中的典型协同模式
不必二选一。主流框架和 JDK 自身都采用“接口定义能力 + 抽象类提供默认实现”的分层设计:
立即学习“Java免费学习笔记(深入)”;
- 先定义接口(如 Collection),明确“能做什么”(add/remove/size 等)
- 再提供抽象类(如 AbstractCollection),封装通用实现(如 contains() 基于 iterator() 实现)
- 具体实现类(如 ArrayList)只专注差异化逻辑(数组扩容、随机访问优化)
- 自定义场景中,接口声明业务能力(如 FraudCheckable),抽象类注入横切逻辑(参数校验、埋点日志、熔断兜底)
一个快速判断口诀
写代码前问自己两句话:
- “如果去掉这个父类,子类还‘是’它本来的样子吗?” → 是,就该用抽象类
- “这个功能,将来可能被完全无关的类也需要吗?” → 可能,就该用接口


















