抽象类表达“是什么”的is-a继承关系,接口表达“能做什么”的can-do契约关系;前者承载状态与行为共性,后者仅约定能力契约,支持多维组合。

抽象类表达“是什么”的继承关系,接口表达“能做什么”的契约关系。
语义核心:is-a 与 can-do
抽象类用于建模类之间的本质归属,比如 Animal 是一个抽象类,Dog 和 Cat 继承它,意味着“狗是一种动物”“猫是一种动物”——这是典型的 is-a 关系。这种语义强调共性状态(如 name、age)和共享行为(如 breathe()),也隐含了类的演化谱系。
接口则不关心“它是什么”,只约定“它能干什么”。例如 Runnable、Serializable、Comparable,无论对象是线程、文件还是日期,只要它“能运行”“能序列化”“能比较”,就实现对应接口——这是纯粹的 can-do 关系。它解耦类型身份与能力组合,支持跨域协作。
语义承载:状态与行为分离
抽象类天然携带状态语义:它可以有普通字段、构造器、初始化逻辑,说明“这类对象应具备哪些内在属性”。比如 Vehicle 抽象类定义 speed 和 fuelLevel,子类自然继承这些运行状态。
接口拒绝状态语义:所有字段自动成为 public static final 常量,不许定义实例变量或构造器。它只承诺行为契约,不参与对象生命周期管理。哪怕 Java 8 后支持 default 方法,也只是为向后兼容提供默认行为,而非引入状态。
- 抽象类的语义包含“模板+骨架+初始化”
- 接口的语义严格限定为“协议+能力标签+组合点”
语义扩展:单线继承 vs 多维能力
抽象类的语义受制于单继承结构,它代表一个不可分割的“家族主线”。你无法让一个类同时是“哺乳动物”又是“飞行器”——因为二者若都设计为抽象类,就会冲突。这迫使设计者必须明确主继承路径。
接口的语义天然支持横向叠加。一个类可以既是 Flyable 又是 Swimmable 还是 Edible,不同维度的能力互不干扰。这种语义更贴近现实世界的多重角色,比如“一只鸭子(is-a Bird)同时能飞、能游、能叫(can-do Flyable, Swimmable, Quackable)”。
语义演进:从规范到协作
早期接口近乎纯契约:只有抽象方法,强制实现者从零构建全部行为。它的语义是“你必须提供这些功能”。Java 8 引入 default/static 方法后,接口语义升级为“我们共同约定一套最小可行行为,你可以直接用,也可以重写”。这不改变其契约本质,而是增强协作效率。
抽象类语义始终稳定:它不因语言升级而转向契约导向。即使添加了更多具体方法,它仍是“父类提供基础,子类延续身份”的范式。它不鼓励能力拼装,而强调血脉传承。











