Java 内置观察者模式

📅 2026/7/30 6:20:07 👁️ 阅读次数 📝 编程学习
Java 内置观察者模式

Java 内置观察者模式

一、基础概念

观察者模式属于行为型设计模式,核心思想:
一对多依赖关系:一个目标(被观察者/主题)状态发生变化时,自动通知所有订阅它的观察者,观察者收到通知后执行自身更新逻辑,二者解耦。

Java 早期 JDK 直接提供了一套原生 API 实现观察者模式,位于java.util包下,由两个核心类/接口构成:

  1. java.util.Observable:被观察者(主题、发布者)抽象父类(类,非接口)
  2. java.util.Observer:观察者函数式接口

⚠️ 重要前提:Java 9 开始被标记为@Deprecated(过时废弃),官方不再推荐使用。

二、两大核心组件详解

1. Observer 观察者接口

@FunctionalInterfacepublicinterfaceObserver{/** * 被观察者状态变更,回调此方法通知观察者 * @param o 触发通知的被观察者对象 * @param arg 推送携带的自定义参数(可选) */voidupdate(Observableo,Objectarg);}
  • 函数式接口:可以直接用 Lambda 简写实现
  • 所有观察者必须实现该接口,重写update()接收消息推送

2. Observable 被观察者(主题)

是一个普通类,不能多继承,内部维护了观察者集合,提供订阅、取消订阅、状态变更通知全套方法:

核心成员变量
// 存放所有已订阅的观察者,线程安全容器privatefinalVector<Observer>obs;// 标记被观察者自身状态是否发生改变privatebooleanchanged=false;
常用核心方法
方法作用
void addObserver(Observer o)订阅:添加观察者到集合
void deleteObserver(Observer o)取消订阅:移除指定观察者
void deleteObservers()清空所有观察者
int countObservers()获取当前订阅的观察者数量
protected void setChanged()关键:标记自身状态已修改;只有标记后,才会执行通知
protected void clearChanged()清除状态变更标记
boolean hasChanged()查询是否标记了状态变更
void notifyObservers()无参推送:通知所有观察者,arg=null
void notifyObservers(Object arg)带参数推送:携带数据传给观察者update方法
通知执行流程
  1. 业务修改被观察者数据 → 调用setChanged()打上变更标记
  2. 调用notifyObservers()
  3. 内部遍历所有观察者,挨个调用observer.update(this, 参数)
  4. 通知完毕自动调用clearChanged()清空标记

三、完整代码示例(手把手演示)

场景:公众号(被观察者)发布文章,订阅用户(观察者)收到推送通知

importjava.util.Observable;importjava.util.Observer;// 1. 自定义被观察者:公众号classOfficialAccountextendsObservable{// 发布文章方法publicvoidpublishArticle(Stringcontent){System.out.println("公众号发布新文章:"+content);// 第一步:标记状态发生改变(必须调用!否则不会推送通知)setChanged();// 第二步:推送消息给所有订阅者,携带文章内容notifyObservers(content);}}// 2. 测试主类publicclassObserverDemo{publicstaticvoidmain(String[]args){// 创建被观察者OfficialAccountaccount=newOfficialAccount();// 观察者1:用户AObserveruserA=(obs,arg)->{System.out.println("用户A收到推送:新文章 -> "+arg);};// 观察者2:用户BObserveruserB=(obs,arg)->{System.out.println("用户B收到推送:新文章 -> "+arg);};// 订阅account.addObserver(userA);account.addObserver(userB);// 发布文章,触发通知account.publishArticle("Java观察者模式详解");System.out.println("=====用户B取消订阅后====");// 取消订阅account.deleteObserver(userB);account.publishArticle("Spring源码阅读笔记");}}

运行结果:

公众号发布新文章:Java观察者模式详解 用户B收到推送:新文章 -> Java观察者模式详解 用户A收到推送:新文章 -> Java观察者模式详解 =====用户B取消订阅后==== 公众号发布新文章:Spring源码阅读笔记 用户A收到推送:新文章 -> Spring源码阅读笔记

四、JDK内置实现的优缺点

优点

  1. 开箱即用,无需自己维护观察者集合、订阅/取消订阅、通知遍历逻辑,代码极简
  2. 自带状态变更标记机制,可灵活控制什么时候推送消息
  3. 自带线程安全(底层Vector),基础多线程场景不用自己加锁

致命缺点(也是被废弃的根本原因)

  1. Observable是类,不是接口,违反面向接口编程
    Java是单继承,如果你的业务类已经继承了其他父类,就无法再继承Observable,扩展性极差。

  2. 无法自定义通知顺序、通知策略
    内部遍历Vector有序推送,不能调整观察者回调顺序;同步阻塞推送,一个观察者耗时过长会阻塞后续所有观察者。

  3. 耦合度偏高
    被观察者、观察者都强依赖JDK util包内置类,不方便替换实现。

  4. 无法精细控制通知粒度
    只能全体推送,不支持按话题分组订阅(类似MQ的主题订阅)。

  5. 线程模型单一:默认同步调用,没有异步通知能力。

五、为什么 Java 9+ 废弃这套API?

  1. 设计缺陷:Observable作为具体类,继承限制太大,不符合开闭原则;
  2. 功能简陋:不支持异步、分组订阅、事件类型区分;
  3. 官方推荐替代方案:
    • 小型场景:自己手写观察者模式(自定义主题接口+观察者接口)
    • 企业级开发:使用事件驱动框架:Guava EventBus、Spring 事件监听(ApplicationEvent)、RxJava、消息队列(RabbitMQ/Kafka)

六、对比:手写观察者 vs JDK内置

  1. JDK内置:省事,但受继承约束、扩展性差;
  2. 手写自定义:自己定义Subject主题接口、Observer观察者接口,任意类都能实现,自由度最高,日常开发首选。

七、拓展:Spring中的事件监听(观察者模式最佳实践)

Spring框架基于观察者思想封装了事件机制,彻底规避了JDK原生的所有问题:

  • ApplicationEvent:事件(消息载体)
  • ApplicationEventPublisher:发布者(被观察者)
  • ApplicationListener:事件监听者(观察者)
    支持异步监听、条件监听、注解驱动(@EventListener),是Java后端最常用的观察者落地方案。