面向接口编程的核心是将依赖从具体类型转向行为契约;接口定义“能做什么”,强调能力组合与约定优先,抽象类则定义“是什么”,侧重共性复用与血缘关系。

面向接口编程的核心,不是“多用 interface 关键字”,而是把调用方和实现方之间的依赖关系,从具体类型转向行为契约。接口与抽象类都是支撑这一思想的工具,但它们承担的角色不同——理解这一点,才能真正抓住核心。
接口是行为契约,定义“能做什么”
接口不关心你是谁,只关心你能提供什么能力。比如 Flyable 接口只声明 fly() 方法,不管实现者是鸟、飞机还是无人机。调用方(如飞行调度系统)只依赖这个接口,就能统一处理所有可飞对象,无需知道背后是哪个类。
- 一个类可以实现多个接口,自然支持多种能力组合(例如 Bird implements Flyable, Singable)
- 接口方法默认抽象(Java 8+ 可加 default/static 方法),强调“约定优先”,实现自由
- 接口之间可以多继承(interface A extends B, C),便于能力聚合
抽象类是共性模板,定义“是什么”和“怎么复用”
抽象类表达的是血缘关系:猫 is-a 动物,狗 is-a 动物。它既约束子类必须实现哪些行为(通过抽象方法),又允许共享通用逻辑(比如所有动物都有 name 字段和 breathe() 方法)。
- 抽象类可以有构造方法、成员变量、普通方法,适合封装可复用的状态与逻辑
- Java 单继承限制决定了它不适合表达“多重身份”,但很适合作为领域模型的顶层基类
- 子类继承抽象类,本质上是在扩展同一类事物的具象化分支
面向接口编程如何落地:依赖倒置是关键动作
真正的面向接口编程,体现在代码结构中——高层模块(如业务服务)不依赖低层模块(如数据库操作),而是共同依赖一个抽象(接口)。比如:
- 定义 OrderRepository 接口,声明 save(Order) 和 findById(Long)
- 业务类 OrderService 只持有 OrderRepository 类型的引用,不关心它是 JdbcOrderRepository 还是 MongoOrderRepository
- 运行时通过依赖注入(如 Spring)把具体实现传入,替换成本几乎为零
这种设计让系统更易测试(可注入 Mock 实现)、更易演进(换数据库只需新写一个实现类)、更易协作(前端先按接口定义联调,后端并行开发)。
什么时候选接口?什么时候选抽象类?
看设计意图:
- 想表达“具备某种能力”或“符合某种协议” → 用接口(如 Serializable、Comparable、PaymentProcessor)
- 想表达“属于某一类事物”,且存在共享状态或通用逻辑 → 用抽象类(如 AbstractList、Animal)
- 需要兼顾两者?常见做法是:用接口定义对外契约,用抽象类作为可选的默认实现(如 java.util.AbstractMap 实现了 Map 接口的部分方法)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











