组合关系指类持有另一类实例作为私有final成员,生命周期由宿主管理,部分不能脱离整体存在;需通过构造器创建、禁用public setter、避免循环引用,并显式释放资源。

对象的组合关系,指的是一个类中持有另一个类的实例作为成员变量,且该成员对象的生命周期由宿主对象管理——即“整体-部分”强依赖关系,部分不能脱离整体独立存在。设计时需明确语义、控制所有权、避免循环引用,并通过构造器或 setter 合理初始化。
明确组合语义,区分组合与聚合
组合强调“拥有并负责销毁”,比如 Car 拥有 Engine:Engine 不会脱离 Car 单独存在,Car 销毁时 Engine 也应失效;而聚合(如 Department 拥有 Employee)中 Employee 可被多个部门共享或独立存在。代码上,组合通常不提供对外暴露部分对象的 getter(或仅返回不可变视图),也不允许外部替换该成员。
- 用私有 final 字段声明组合对象,体现强绑定和不可变性
- 在构造器中创建并赋值,确保对象一创建就处于完整状态
- 避免提供 public setter 或返回可变引用的 getter(如不要直接 return this.engine)
通过构造器完成组合对象的创建与注入
这是最常见也最安全的方式,保证组合对象随宿主一同诞生,生命周期天然一致。适合组合对象无外部依赖、可由宿主完全掌控初始化逻辑的场景。
示例:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
public class Car {
private final Engine engine;
private final List<wheel> wheels;
public Car() {
this.engine = new Engine(); // 内部创建
this.wheels = Arrays.asList(new Wheel(), new Wheel(),
new Wheel(), new Wheel());
}
// 也可接受参数,但仍是宿主负责创建或验证有效性
public Car(Engine engine, List<wheel> wheels) {
this.engine = Objects.requireNonNull(engine);
this.wheels = Collections.unmodifiableList(
new ArrayList(Objects.requireNonNull(wheels))
);
}
}
</wheel></wheel>
谨慎处理资源释放与 finalize(必要时)
Java 无析构函数,但若组合对象持有显式资源(如文件句柄、网络连接),需提供显式的 close() 方法,并在宿主的 close() 中调用它。不要依赖 finalize —— 它不保证执行时机,已被标记为废弃。
- 实现 AutoCloseable 接口,支持 try-with-resources
- close() 方法应幂等,多次调用不抛异常
- 组合对象的 close() 应在宿主 close() 中按依赖顺序调用(先子后父,或按创建逆序)
示例片段:
public class DatabaseConnection implements AutoCloseable {
private final Connection conn;
public DatabaseConnection() { this.conn = DriverManager.getConnection(...); }
@Override
public void close() {
if (conn != null) {
try { conn.close(); } catch (SQLException ignored) {}
}
}
}
public class ReportGenerator implements AutoCloseable {
private final DatabaseConnection db;
private final FileExporter exporter;
public ReportGenerator() {
this.db = new DatabaseConnection();
this.exporter = new FileExporter();
}
@Override
public void close() {
// 先关 db,再关 exporter(假设 exporter 依赖 db 数据)
if (db != null) db.close();
if (exporter != null) exporter.close();
}
}
避免循环组合与隐式强耦合
两个类互相持对方实例(A 组合 B,B 又组合 A),会导致构造困难、内存泄漏、序列化失败。解决方式包括:
- 用弱引用(WeakReference)缓存非必需的反向引用
- 将反向访问改为方法参数传递(如 b.process(a) 而非 b.setA(a))
- 引入第三方协调者,打破直接依赖
- 改用聚合或关联(用 ID 或工厂方法延迟获取)替代强组合
例如,Order 和 OrderItem 之间:Order 持有 List
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










