组合关系的核心是“控住生命周期和访问路径”,要求部分对象由整体对象强拥有、不可外部持有或修改,通过构造器注入+private final字段建立绑定,禁止暴露原始引用,避免循环依赖,并依业务语义区分组合与聚合。

封装关联对象时,关键不是“藏起来”,而是“控住生命周期和访问路径”。组合关系要求部分对象(part)完全由整体对象(whole)管理,不能被外部随意持有、修改或复用。处理得当,才能真正体现“强拥有”语义。
用构造器注入 + 私有 final 字段建立强绑定
组合关系的起点是初始化阶段的控制权。不要在类内部懒加载 part,也不要通过 setter 允许后期替换。
- 在 whole 的构造器中直接 new part,或接收已创建的 part 实例(但不暴露其引用)
- 将 part 字段声明为 private final,杜绝后续赋值
- 避免提供返回该 part 原始引用的 getter 方法——比如
public Engine getEngine()是危险的;若必须查看,可提供只读接口或副本方法(如Collections.unmodifiableList(this.parts))
禁止外部绕过业务逻辑直接操作 part
封装不只是字段加 private,更要防止外部拿到 part 后破坏整体一致性。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 如果 part 是集合(如 List
),不要返回原始 list 引用,否则调用方可能 add/remove 导致状态错乱 - 对 part 的所有操作应通过 whole 提供的受控方法进行,例如
team.addPlayer(player)而非team.getPlayers().add(player) - 必要时在 whole 的清理逻辑(如 close() 或自定义销毁方法)中主动释放 part 占用的资源(线程、IO、监听器等)
用单向引用避免循环依赖和内存泄漏
组合强调 ownership,而非双向绑定。让 part 持有对 whole 的引用容易引发循环引用、GC 困难或状态不一致。
- Player 不需要持有 Team 引用;它的归属由所处的 Players 集合位置隐式确定
- Team 持有 Players,Players 持有 List
,层级清晰、责任分明 - 如确需反查(如 player.getTeamName()),可通过回调或事件机制解耦,而非直接持有引用
区分组合与聚合,从语义出发而非写法
是否是组合,不取决于代码里有没有 new,而取决于业务规则:这个 part 是否只能属于这一个 whole?删掉 whole 时,part 是否自然失去意义?
- Car 和 Engine 是组合:引擎专为这辆车设计,车报废后引擎一般不再单独使用
- Order 和 PaymentService 是聚合:支付服务可被多个订单复用,生命周期独立
- 写代码前先问一句:“如果我把 whole 销毁了,这个 part 还有没有独立存在的价值?”答案决定你该用组合还是聚合
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










