Java对象关系靠引用实现,通过字段、局部变量等持有对方内存地址;无内置关系管理系统,常见模式有组合、聚合、依赖、关联;Spring的@Autowired仅自动注入引用,不定义协作语义。

Java里对象关系靠引用,不是靠“绑定”或“注册”
Java中对象之间没有内置的“关系管理系统”,所有关联都通过字段(private MyService service)或局部变量(MyDao dao = new MyDao();)持有对方的引用实现。这不是配置或注解驱动的“关系”,而是直接的内存地址指向——只要一个对象持有另一个对象的引用,它们就算建立了协作关系。
- 常见错误:以为用
@Autowired或new就自动产生了“双向关系”,其实只是单向引用;若没在另一端存回引用,就不存在反向访问能力 - 没有引用即无关系:局部变量声明后未赋值、字段为
null、方法返回null,都会导致运行时NullPointerException - 循环引用不报错但危险:A 持有 B,B 又持有 A,JVM 能正常运行,但 GC 无法回收(除非是弱引用),容易引发内存泄漏
四种典型协作模式对应不同生命周期管理
对象怎么持有对方,直接影响谁创建、谁销毁、谁负责传递依赖:
-
组合(Composition):A 类内部
new出 B 实例(如private final DatabaseConnection conn = new DatabaseConnection();),B 的生命周期完全由 A 控制;B 不该暴露给外部,也不该被其他类复用 -
聚合(Aggregation):A 接收 B 的实例(如构造器参数
public OrderProcessor(OrderValidator validator)),B 由外部创建并传入,A 不负责其生命周期;适合可替换、可测试的协作 -
依赖(Dependency):B 仅作为方法参数出现(如
void save(User user, Transaction tx)),调用完即丢弃,无状态持有;最轻量,但频繁创建开销需留意 -
关联(Association):A 和 B 都是独立存在、长期存活的对象,通过 setter 或字段互相引用(如
user.setDepartment(dept)+dept.addUser(user));必须手动维护两端一致性,否则数据逻辑错乱
Spring 的 @Autowired 不是“建立关系”,而是“自动填引用”
@Autowired 本身不定义关系语义,它只是让 Spring 容器在启动时,按类型或名称把已注册的 Bean 填进你的字段或构造器。它解决的是“谁来创建并传入依赖”,而不是“对象之间该怎么协作”:
- 字段注入(
@Autowired private UserService userService;)会导致类与容器耦合,且无法在构造时保证非空,单元测试难模拟 - 推荐构造器注入:
public UserController(@NonNull UserService userService),明确依赖、不可变、易测 - 如果两个 Bean 相互
@Autowired,Spring 默认会用三级缓存解决循环依赖,但仅限于 singleton 作用域;prototype 或其他作用域下直接抛BeanCurrentlyInCreationException - 注意
@Lazy:对循环依赖场景,加@Lazy可延迟代理对象初始化,避免早期加载失败
对象协作出问题?先查引用是否存在、是否为 null、是否被意外覆盖
90% 的协作异常不是设计问题,而是引用管理疏忽:
立即学习“Java免费学习笔记(深入)”;
- 空指针:检查字段是否漏了初始化、setter 是否被调用、构造器是否传入了
null - 状态不一致:比如订单对象调用了
setPayment(payment),但 Payment 对象没反向设置order字段,后续从 Payment 查订单就拿不到 - 并发修改:多个线程同时调用
addUser()和removeUser()而没加锁或用线程安全集合,导致关系丢失 - 忘记清理:监听器、回调、观察者注册后没在销毁时
remove,造成内存泄漏和重复触发
真正复杂的地方不在语法,而在谁该拥有引用、何时建立、何时断开、是否需要同步——这些得结合业务生命周期去判断,不能只看代码能不能跑通。










