抽象类通过封装公共逻辑、提供默认实现和模板方法,解决接口实现中的代码冗余问题;它支持状态管理与构造器初始化,适用于多实现且存在共性行为的场景,优于仅含default方法的接口。

接口定义契约,抽象类负责复用——这是解决接口实现中冗余代码的核心思路。当多个实现类需共享相同逻辑(如日志、校验、模板流程),直接在每个类里重复写,既违背 DRY 原则,又增加维护成本。抽象类恰好能作为“中间层”,对接口提供默认行为,同时保留子类的定制能力。
用抽象类封装公共逻辑
把多个实现类共有的代码(比如参数非空检查、结果统一封装、调用前/后钩子)抽到抽象类中,让具体实现类只关注差异部分。
- 抽象类实现接口,但不声明所有方法为 abstract;可提供部分默认实现
- 将可复用的辅助方法(如 buildResponse()、logRequest())设为 protected,供子类按需调用或重写
- 保留关键业务方法为 abstract,强制子类实现差异化逻辑
避免过度设计:抽象类 ≠ 通用基类
不是所有接口都值得配抽象类。只有当已有两个及以上实现、且存在明确可提取的共性行为时,才引入抽象类。
- 若当前只有一个实现类,优先用组合(如引入工具类或策略对象)而非提前抽象
- 抽象类不应承担过多职责——比如把数据库操作、缓存、鉴权全塞进去,会降低可测试性和可读性
- 警惕“伪抽象”:仅含空方法或全是 final 方法的抽象类,实际未提供有效复用,不如直接用接口
与默认方法的取舍:何时选抽象类?
Java 8+ 接口支持 default 方法,但它有局限:不能持有状态、无法调用构造器、不能使用 protected 成员。抽象类更灵活。
- 需要初始化字段(如配置对象、客户端实例)→ 用抽象类构造器完成
- 需在不同实现间共享状态(如计数器、上下文缓存)→ 抽象类可定义 protected 字段
- default 方法适合纯函数式、无状态的通用逻辑(如集合转换、简单校验);复杂流程建议交给抽象类
配合模板方法模式提升可扩展性
抽象类定义骨架流程(template method),把可变环节留给子类实现,既保证结构统一,又支持灵活定制。
- 例如接口 PaymentProcessor,抽象类定义 execute() 流程:校验 → 扣款 → 记账 → 发通知,其中 doDeduct() 和 doRecord() 为 abstract
- 子类只需实现扣款和记账细节,无需关心整体顺序和异常兜底
- 后续新增支付方式,只要继承该抽象类,就能复用校验、通知等稳定环节











