Java原生Observer/Observable因继承限制和弃用已不推荐,应手写接口+Subject抽象类实现解耦观察者模式,配合CopyOnWriteArrayList保障线程安全,并可选用Spring@EventListener替代。

Java里用 Observer 和 Observable 类写观察者模式为什么总不生效
因为 java.util.Observable 是抽象类,且从 Java 9 开始被标记为 @Deprecated,JDK 官方明确不再推荐使用。更关键的是:它要求被观察者必须继承 Observable,而 Java 不支持多重继承,一旦你的类已继承其他父类(比如 Thread 或某个业务基类),这条路就走不通。
实际开发中几乎没人用这套原生 API,真正稳定、灵活的方案是手写接口 + 自定义通知逻辑。
自己定义 Observer 接口和 Subject 抽象类更可靠
核心是解耦:观察者只关心“被通知”,不依赖具体被观察者类型;被观察者只维护观察者列表,不绑定任何实现细节。
-
Observer接口定义update(Subject subject, Object data)方法,接收通知源和数据 -
Subject抽象类提供addObserver()、removeObserver()、notifyObservers()基础能力 - 具体被观察者(如
WeatherStation)继承Subject,在状态变化时调用notifyObservers(this, currentData) - 具体观察者(如
PhoneDisplay)实现Observer,在update()中处理业务逻辑
这样既避免继承限制,又支持泛型扩展(比如让 update() 接收 Event 类型参数)。
立即学习“Java免费学习笔记(深入)”;
用 java.util.concurrent.CopyOnWriteArrayList 存储观察者列表
多线程环境下,遍历观察者列表的同时可能有其他线程在增删监听器——直接用 ArrayList 会触发 ConcurrentModificationException。
CopyOnWriteArrayList 的行为特点是:
- 读操作无锁、高性能,适合频繁通知场景
- 写操作(
add/remove)会复制整个数组,适合观察者数量少、变更不频繁的情况 - 通知时遍历的是快照,不会因中途修改导致异常或漏通知
public abstract class Subject {
private final List observers = new CopyOnWriteArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void notifyObservers(Object data) {
for (Observer o : observers) {
o.update(this, data);
}
}
}
Spring 的 @EventListener 是观察者模式的高级替代方案
如果你项目已引入 Spring,没必要重复造轮子。Spring 的事件机制本质就是观察者模式的生产级实现:
- 发布事件用
ApplicationEventPublisher.publishEvent(new CustomEvent(source, data)) - 监听事件只需一个带
@EventListener的方法,参数类型即事件类型 - 天然支持异步(加
@Async)、事务绑定(@TransactionalEventListener)、条件过滤(condition = "#event.status == 'OK'") - 所有监听器自动注册到容器,无需手动管理生命周期
注意:自定义事件类必须继承 ApplicationEvent,否则 Spring 不识别;非 Spring 环境下该方案不可用。
手写观察者最易忽略的点是状态同步时机——不是“只要变了就立刻通知”,而是要判断是否真的需要广播(比如连续三次温度更新只差 0.1℃,应合并或节流)。这层逻辑得由具体业务决定,框架只管“怎么发”,不管“什么时候发”。










