is-a 表示继承关系,强调“本质上就是”,如猫 is-a 动物;has-a 表示组合/聚合,强调“拥有并协作”,如订单 has-a 支付处理器;like-a 非标准术语,实为接口实现(is-a interface)与组合(has-a 专属组件)的混合使用。

Java 中没有标准术语 brings-a,它不是面向对象设计中的正式关系类型,很可能是对 has-a(组合/聚合)或 uses-a(依赖)的误写或口语化表达。实际开发中,只有 is-a(继承)和 has-a(组合)是明确、常用且语义清晰的设计关系。理解它们的适用边界,直接决定类结构是否松耦合、可维护、可扩展。
is-a:只在“确实是某种类型”时才用
这个关系强调身份认同——子类在逻辑上就是父类的一种具体形态,能完全替代父类使用(符合里氏替换原则)。不是“看起来像”或“用到了”,而是“本质上就是”。
- 猫 is-a 动物 → 可以传给接受 Animal 类型的任何方法,且行为合理
- ArrayList is-a List → 它实现了 List 接口的所有契约,客户端无需知道它是 ArrayList
- 但“订单 is-a 支付处理器”就错了——订单不“是”支付处理器,它只是“用”或“有”一个
has-a:绝大多数复用场景的真实选择
当一个类“拥有”另一个类的实例,并通过字段持有、委托调用完成功能时,就是 has-a。它不强求身份一致,只关注职责协作。
- Order has-a PaymentProcessor → 订单包含支付处理器,可随时换为 AlipayProcessor 或 WechatProcessor
- Car has-a Engine → 引擎可独立测试、替换、升级,不影响 Car 的核心定义
- 这种关系天然支持运行时切换、Mock 测试、解耦重构,避免继承带来的脆弱基类问题
别把“用到了”当成“是某种类型”
如果某个类只是临时用到另一个类(比如方法参数、局部变量),属于 uses-a(依赖),更不该用继承。强行用 is-a 会污染类型体系:
- ReportGenerator 使用 ExcelWriter 生成报表 → 这是依赖,不是 is-a;ReportGenerator 不“是”ExcelWriter
- 把 ExcelWriter 设为 ReportGenerator 的父类,会导致 ReportGenerator 被迫继承无关的 writeToFile()、setSheetName() 等方法,违背单一职责
- 正确做法:把 ExcelWriter 作为 ReportGenerator 的构造参数或 setter 注入,即 has-a + 依赖注入
like-a 不是标准术语,本质是接口实现 + 组合
有些资料提到 like-a(如“手机 like-a 计算机”),其实反映的是:既有部分共性(可通过接口或抽象类建模),又有独特能力(需额外组合或实现)。这不是新关系,而是 is-a(对接口/抽象类)与 has-a(对专属组件)的混合使用:
- Phone 实现 Computer 接口(体现计算能力 like-a)
- Phone 同时 has-a TelecomModule(处理通话,无法从 Computer 继承)
- 这样既复用通用契约,又保持职责正交,比单靠继承更稳健
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











