选抽象类还是接口取决于设计意图:表达“是什么”用抽象类,强调“能做什么”用接口;抽象类适合有父子层级、共享状态和构造逻辑的场景,接口适用于跨类型能力组合与松耦合扩展。

选接口还是抽象类,关键看你想表达“是什么”还是“能做什么”。不是语法记不住,而是设计意图没对上。
看继承关系:有明确父子层级就用抽象类
当几个类天然属于同一类事物,共享字段、构造逻辑或通用方法时,抽象类更合适。比如“支付方式”这个场景:
- 微信支付、支付宝、银行卡支付,都是“支付”,共用订单号、金额、创建时间等字段
- 都有“校验签名”“记录日志”这类可复用的逻辑
- 但“发起扣款”动作各不相同,必须由子类自己实现
这时定义一个 abstract class Payment,把公共属性和基础方法写进去,再留一个 abstract void execute() 让子类覆盖——结构清晰,代码不重复。
看能力组合:需要跨类型加功能就用接口
当不同类之间没有继承关系,却要统一支持某种行为,接口就是唯一选择。比如:
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 订单类(Order)、用户类(User)、配置类(Config)都要能被序列化传输 → 实现 Serializable
- 商品(Product)、优惠券(Coupon)、物流单(Shipment)都需要支持按时间排序 → 实现 Comparable<T>
- 一个无人机类既要飞(Flyable),又要拍视频(Recordable),还要联网(Connectable)→ 同时 implements 多个接口
这些能力跟“它是什么”无关,只关心“它能干什么”。接口不破坏原有类结构,也不强求共享状态,松耦合优势明显。
看是否需要初始化或保存状态
如果子类实例化时必须设置某些初始值,比如动物要有名字、车辆要有品牌、报表要有模板路径——抽象类可以定义构造方法,通过 super(name) 统一完成初始化;接口做不到这点,它连构造器都不允许。
同理,如果多个子类要共享某个可变状态(如缓存容器、连接池引用、配置上下文),抽象类里声明 protected Map<String, Object> context 是自然的选择;接口只能放 public static final 常量,没法存运行时状态。
看未来扩展性:优先接口,留足余地
哪怕当前只有两个类用到某项能力,只要它可能被更多不相关类复用,就定义成接口。比如你写了“导出为PDF”功能,先抽象出 Exportable 接口。之后报表、合同、体检报告等新类要导出,直接 implements 就行,不用动已有继承链。
反过来,如果一开始就用抽象类封装导出逻辑,后续想让“非报表类”也导出,就得重构——要么把抽象类拆成接口+工具类,要么引入额外中间层,徒增复杂度。

















