接口定义契约以隔离行为变化,抽象类封装共性流程并预留钩子点;组合优于继承,避免抽象层臃肿;抽象需适度,应基于已知变化点而非猜测。

接口和抽象类是 Java 中支撑系统弹性的两大基石,关键不在于“用哪个”,而在于“怎么用才让后续变更不伤筋动骨”。核心思路是:用抽象封住稳定部分,用实现放开变化部分。
用接口定义契约,隔离变化方向
接口只声明“做什么”,不涉及“怎么做”,天然适合应对行为维度的扩展。比如乘车服务新增 VIP 类型,只需新增一个 VipCar 实现类,完全不碰原有逻辑:
- 定义统一接口:
public interface Car { void drive(); } - 已有实现:
Jingji、Shuxing各自保持不变 - 新增需求时,只加
Vip implements Car,不改任何已有类 - 调用方(如
Test)只依赖Car接口,无需感知新类型
用抽象类封装共性逻辑,预留钩子点
当多个子类共享流程骨架但细节不同,抽象类比接口更合适。它既能提供默认实现,又能强制子类实现关键步骤:
- 例如支付流程:抽象类
AbstractPayment可封装通用校验、日志、回调逻辑 - 定义抽象方法
doPay()和getFeeRule(),由WechatPayment、AlipayPayment分别实现 - 未来加“分期支付”?只需新增子类,复用父类的通用流程,不重复写校验或日志
- 若某类需跳过某步校验,可覆写父类 protected 方法,不影响其他子类
组合优于继承,避免抽象层被“撑爆”
抽象类一旦承担太多职责,就容易变成“万能基类”,反而阻碍扩展。这时应把能力拆成接口,用组合组装:
- 不要让
Order抽象类同时管“计算运费”、“生成发票”、“发短信通知” - 改为定义
FeeCalculator、InvoiceGenerator、Notifier等接口 -
Order持有这些接口的引用,运行时注入具体实现(如ExpressFeeCalculator) - 新增“国际运费规则”?只加新实现类,不改
Order,也不动其他计算器
抽象要适度,过早抽象反而是负担
不是所有地方都需要接口或抽象类。如果当前只有 1 种实现,且没有明确的扩展信号,强行抽象会增加理解成本:
- 初期只有一个
FileLogger,没计划加DBLogger或RemoteLogger?先不用抽接口 - 等第二类日志器出现时,再提取
Logger接口,此时抽象才有真实依据 - 抽象的价值来自对“已知变化点”的识别,而不是对“可能变化”的猜测
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











