接口管行为契约,抽象类管代码复用;接口强调“能做什么”(can-do),抽象类强调“是什么”(is-a);前者支持多实现、无状态、重规范,后者支持单继承、可含状态与构造器、重复用。

接口和抽象类不是“选哪个更好”,而是“解决什么问题”。接口管行为契约,抽象类管代码复用——弄清这个出发点,选择就自然了。
看设计语义:是“能做什么”还是“是什么”
接口表达的是 can-do(能做什么),比如 Runnable、Serializable、Swimmable,不关心对象是谁,只关心它能不能跑、能不能序列化、会不会游泳。一个类可以同时实现多个接口,说明它可以扮演多个角色。
抽象类表达的是 is-a(是什么),比如 Animal、ReportExporter、HttpServlet,强调类型归属和层级关系。狗是动物,PDF导出器是一种报表导出器,它们天然共享状态(如 name、config)、流程(如查数→加工→写文件)和默认行为(如通用日志记录)。
看语法能力:能放什么、能怎么用
以下差异直接影响编码方式:
- 抽象类可含构造器、实例变量、普通方法、抽象方法;接口只能有常量(
public static final)、抽象方法、默认方法(default)、静态方法(static) - 一个类只能
extends一个抽象类,但可以implements多个接口 - 抽象类支持单继承下的模板复用;接口支持跨继承体系的能力扩展
- 抽象类中的抽象方法必须由非抽象子类实现;接口中未被默认方法覆盖的抽象方法,也必须由实现类提供
看典型场景:什么时候该用哪一个
优先用抽象类的情况:
- 多个子类有大量重复字段或逻辑(如共用数据库连接、统一日志、通用校验),且存在明确的父类概念
- 需要定义一套固定流程骨架,仅留少数环节由子类定制(即模板方法模式,如
export()流程中process()和writeToFile()抽象) - 需要在初始化时做些共用操作(比如读配置、检查环境),这时构造器就派上用场
优先用接口的情况:
- 不同类之间没有继承关系,但需统一对外行为(如支付渠道:微信、支付宝、银联都实现
Payable) - 想为已有类添加新能力,又不能动它的继承链(如让一个已继承
Thread的类也具备可序列化能力,只需实现Serializable) - 团队协作中约定调用方与实现方的契约边界(比如网关层只依赖
OrderService接口,不管背后是本地DB还是远程RPC)
特别注意:别混淆职责
别用抽象类去定义纯行为规范——它太重,带状态、带构造器、带实现,容易把使用者绑死;也别用接口去封装共用字段或复杂流程——它没构造器、没实例变量、不支持初始化逻辑,强行塞进去只会导致每个实现类重复写一堆样板代码。
Java 8 后接口支持默认方法,但这不是为了替代抽象类,而是为了向后兼容地增强契约(比如给 List 新增 sort() 方法,不用改所有实现类)。真正要复用逻辑,还是得靠抽象类。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











