在Java中如何实现观察者模式_JavaObserverPattern对象交互解析
发布时间 - 2026-02-02 00:00:00 点击率:次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.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℃,应合并或节流)。这层逻辑得由具体业务决定,框架只管“怎么发”,不管“什么时候发”。
# java
# app
# 无锁
# 为什么
# spring
# Object
# 父类
# 继承
# 接口
# 多重继承
# Event
# 泛型
# 线程
# 多线程
# Thread
# 对象
# 事件
# this
# 异步
# 的是
# 抽象类
# 遍历
# 自定义
# 绑定
# 如果你
# 什么时候
# 只需
# 没人
# 推荐使用
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
用yum安装MySQLdb模块的步骤方法
微信小程序 require机制详解及实例代码
浅谈redis在项目中的应用
如何在IIS中新建站点并解决端口绑定冲突?
香港服务器建站指南:外贸独立站搭建与跨境电商配置流程
南京网站制作费用,南京远驱官方网站?
用v-html解决Vue.js渲染中html标签不被解析的问题
非常酷的网站设计制作软件,酷培ai教育官方网站?
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
制作企业网站建设方案,怎样建设一个公司网站?
Laravel如何实现API速率限制?(Rate Limiting教程)
iOS中将个别页面强制横屏其他页面竖屏
手机怎么制作网站教程步骤,手机怎么做自己的网页链接?
Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全
如何用PHP工具快速搭建高效网站?
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
百度浏览器ai对话怎么关 百度浏览器ai聊天窗口隐藏
香港服务器建站指南:免备案优势与SEO优化技巧全解析
Windows11怎样设置电源计划_Windows11电源计划调整攻略【指南】
Python文件操作最佳实践_稳定性说明【指导】
Laravel如何实现本地化和多语言支持?(i18n教程)
Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】
如何用搬瓦工VPS快速搭建个人网站?
JavaScript如何实现错误处理_try...catch如何捕获异常?
合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?
如何在宝塔面板中修改默认建站目录?
Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决
Laravel如何实现模型的全局作用域?(Global Scope示例)
Laravel如何使用Collections进行数据处理?(实用方法示例)
如何基于云服务器快速搭建个人网站?
Python函数文档自动校验_规范解析【教程】
Laravel Eloquent:优雅地将关联模型字段扁平化到主模型中
如何快速查询域名建站关键信息?
Laravel如何处理文件下载请求?(Response示例)
JavaScript模板引擎Template.js使用详解
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
大连 网站制作,大连天途有线官网?
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
Laravel怎么使用artisan命令缓存配置和视图
网站制作大概多少钱一个,做一个平台网站大概多少钱?
MySQL查询结果复制到新表的方法(更新、插入)
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
北京网站制作的公司有哪些,北京白云观官方网站?
php静态变量怎么调试_php静态变量作用域调试技巧【解答】
Python结构化数据采集_字段抽取解析【教程】
如何用免费手机建站系统零基础打造专业网站?
韩国服务器如何优化跨境访问实现高效连接?
Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】
Python面向对象测试方法_mock解析【教程】
Laravel如何发送系统通知_Laravel Notifications实现多渠道消息通知


