接口定义纯能力契约,只声明必须实现的方法;抽象类提供可复用的结构骨架,含抽象方法、具体方法和构造器;二者协同:接口定能力,抽象类搭骨架,实现类填细节。

接口与抽象类在 Java 中定义行为契约的方式不同,关键不在“丰富程度”,而在于“契约类型”和“使用意图”。接口专注统一能力声明,抽象类侧重可复用的结构骨架。
接口:用方法签名 + default/static 方法定义能力边界
接口本质是纯契约——它不承诺怎么实现,只规定必须提供哪些能力。Java 8 起支持 default 和 static 方法,让接口能附带轻量级默认逻辑,但这些不能替代实现类的主体职责。
- 所有方法默认 public abstract,无需显式写;只写方法签名即可表达强制实现义务
- default 方法用于向后兼容扩展(比如 Collection 接口新增 stream()),但不可访问 this 状态或子类私有成员
- static 方法属于接口自身工具,不参与多态,调用时必须用 InterfaceName.method() 形式
- 字段只能是 public static final 常量,严禁存实例状态或可变对象引用
抽象类:用抽象方法 + 普通方法 + 构造器定义可继承的结构契约
抽象类不是契约容器,而是半成品模板。它允许混入具体实现、共享状态、控制初始化流程,适合表达“是什么”层面的共性。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- abstract 方法强制子类填补逻辑空缺,相当于契约中的“必答题”
- 普通方法提供开箱即用的功能(如 HttpServlet 的 doGet/doPost 封装了请求解析逻辑)
- 构造方法虽不能直接 new,但供子类 super() 调用,可完成共用初始化(如 Animal(name) 设置基础属性)
- 支持 protected 成员、字段、工具方法,子类可直接读写或复用,这是接口无法提供的信任层级
组合使用:接口定能力,抽象类搭骨架,实现类填细节
真实项目中,二者常协同工作。例如定义一个消息处理器体系:
- 先用 MessageHandler 接口声明 handle(Message msg) 和 supports(Type t) 两个核心契约
- 再写 AbstractMessageHandler 抽象类,封装日志记录、异常包装、上下文传递等通用逻辑,并把 supports() 默认实现为 true
- 具体处理器如 EmailHandler、SmsHandler 只需继承抽象类并实现 handle(),自动获得健壮基座
避免契约污染的几个关键点
行为契约一旦发布就难修改,定义时需克制随意添加的冲动:
- 接口方法别返回 Optional —— 它暗示“可能为空”,破坏了契约的确定性语义
- default 方法别试图模拟 protected 行为(如读取子类字段),那说明该逻辑本该放在抽象类里
- 多个接口若存在同名方法但签名冲突(如返回类型不同),编译器直接拒绝实现,这不是设计灵活,而是契约矛盾
- 抽象类里不要塞满 abstract 方法而无任何具体逻辑,那就退化成接口了;反过来,接口里堆砌大量 default 方法也违背其轻量契约定位
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










