当前位置: 首页 > news >正文

【设计模式】【行为型模式】观察者模式(Observer)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

  • 一、入门
    • 什么是观察者模式?
    • 为什么要观察者模式?
    • 怎么实现观察者模式?
  • 二、观察者模式在源码中运用
    • Java 中的 java.util.Observer 和 java.util.Observable
      • Observer和Observable的使用
      • Observer和Observable的源码实现
    • Spring 框架中的事件机制
      • Spring事件机制的使用
      • Spring的事件机质的源码实现
  • 三、总结
    • 观察者模式的优点
    • 观察者模式的缺点
    • 观察者模式的适用场景
  • 参考

一、入门

什么是观察者模式?

观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。

为什么要观察者模式?

假设我们正在开发一个天气预报系统,其中:

  • WeatherStation(气象站):负责收集天气数据(如温度、湿度等)。
  • Display(显示设备):负责显示天气数据,比如手机App、电子屏等。

当气象站的数据更新时,所有显示设备都需要实时更新显示内容。
下面是没有观察者模式时的实现:

class WeatherStation {private float temperature;private float humidity;private PhoneDisplay phoneDisplay;private TVDisplay tvDisplay;public void setPhoneDisplay(PhoneDisplay phoneDisplay) {this.phoneDisplay = phoneDisplay;}public void setTVDisplay(TVDisplay tvDisplay) {this.tvDisplay = tvDisplay;}public void removePhoneDisplay() {phoneDisplay = null;}public void removeTVDisplay() {phoneDisplay = null;}public void setMeasurements(float temperature, float humidity) {this.temperature = temperature;this.humidity = humidity;// 手动调用显示设备的更新方法if (phoneDisplay != null) {phoneDisplay.update(temperature, humidity);}if (tvDisplay != null) {tvDisplay.update(temperature, humidity);}}
}

紧耦合
如果没有观察者模式,气象站需要直接知道所有显示设备的存在,并手动调用它们的更新方法。例如:
问题:

  • 气象站和显示设备之间是紧耦合的,气象站需要知道所有显示设备的具体实现。
  • 如果新增一个显示设备(比如智能手表),需要修改气象站的代码,违反了开闭原则(对扩展开放,对修改关闭)。

难以动态管理依赖
如果显示设备需要动态添加或移除(比如用户关闭了某个显示设备),气象站需要手动管理这些设备的引用。

扩展性差
如果未来需要支持更多类型的观察者(比如日志记录器、报警系统等),气象站的代码会变得越来越臃肿,难以维护。

怎么实现观察者模式?

在观察者模式中有如下角色:

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

【案例】天气站 - 改
在这里插入图片描述
Observer观察者: Observer接口

interface Observer {void update(float temperature, float humidity);
}

Subject主题: subject接口

interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}

实现具体主题(气象站): WeatherStation

class WeatherStation implements Subject {private List<Observer> observers = new ArrayList<>();private float temperature;private float humidity;@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity);}}public void setMeasurements(float temperature, float humidity) {this.temperature = temperature;this.humidity = humidity;notifyObservers(); // 通知所有观察者}
}

实现具体观察者(显示设备): PhoneDisplay类和TVDisplay

class PhoneDisplay implements Observer {@Overridepublic void update(float temperature, float humidity) {System.out.println("手机显示:温度 = " + temperature + ",湿度 = " + humidity);}
}class TVDisplay implements Observer {@Overridepublic void update(float temperature, float humidity) {System.out.println("电视显示:温度 = " + temperature + ",湿度 = " + humidity);}
}

测试类

public class WeatherApp {public static void main(String[] args) {WeatherStation weatherStation = new WeatherStation();Observer phoneDisplay = new PhoneDisplay();Observer tvDisplay = new TVDisplay();weatherStation.registerObserver(phoneDisplay);weatherStation.registerObserver(tvDisplay);// 更新天气数据weatherStation.setMeasurements(25.5f, 60.0f);// 移除一个观察者weatherStation.removeObserver(tvDisplay);// 再次更新天气数据weatherStation.setMeasurements(26.0f, 58.0f);}
}

运行结果

手机显示:温度 = 25.5,湿度 = 60.0
电视显示:温度 = 25.5,湿度 = 60.0
手机显示:温度 = 26.0,湿度 = 58.0

二、观察者模式在源码中运用

Java 中的 java.util.Observer 和 java.util.Observable

Java 标准库中提供了观察者模式的实现,分别是 Observer 接口和 Observable 类。

  • Observable 是被观察者的基类,内部维护了一个观察者列表,并提供了 addObserver、deleteObserver 和 notifyObservers 方法。
  • Observer 是观察者接口,定义了 update 方法,用于接收通知。

Observer和Observable的使用

被观察者(具体主题):WeatherData

// 被观察者(主题)
class WeatherData extends Observable {private float temperature;private float humidity;public void setMeasurements(float temperature, float humidity) {this.temperature = temperature;this.humidity = humidity;setChanged(); // 标记状态已改变notifyObservers(); // 通知观察者}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}
}

观察者(具体观察者): Display

// 观察者
class Display implements Observer {@Overridepublic void update(Observable o, Object arg) {if (o instanceof WeatherData) {WeatherData weatherData = (WeatherData) o;float temperature = weatherData.getTemperature();float humidity = weatherData.getHumidity();System.out.println("当前温度: " + temperature + ",湿度: " + humidity);}}
}

测试

public class ObserverPatternDemo {public static void main(String[] args) {WeatherData weatherData = new WeatherData();Display display = new Display();weatherData.addObserver(display); // 注册观察者weatherData.setMeasurements(25.5f, 60.0f); // 更新数据并通知观察者}
}

输出结果

当前温度: 25.5,湿度: 60.0

Observer和Observable的源码实现

观察者:Observer类,入参Observable o:被观察的对象(主题)和 Object arg:传递给观察者的额外参数(可选)。

public interface Observer {void update(Observable o, Object arg);
}

主题Observable类。
我们可以看到Vector<Observer>存储观察者列表。并且因为加了synchronized关键字,这些方法都是线程安全的。notifyObservers方法会遍历观察者列表,并调用每个观察者的update方法。

public class Observable {// 标记对象是否已改变private boolean changed = false;// 观察者列表(使用 Vector 保证线程安全)private Vector<Observer> obs;public Observable() {obs = new Vector<>();}// 添加观察者public synchronized void addObserver(Observer o) {if (o == null)throw new NullPointerException();if (!obs.contains(o)) {obs.addElement(o);}}// 删除观察者public synchronized void deleteObserver(Observer o) {obs.removeElement(o);}// 通知所有观察者(无参数)public void notifyObservers() {notifyObservers(null);}// 通知所有观察者(带参数)public void notifyObservers(Object arg) {Observer[] arrLocal;// 同步块,确保线程安全synchronized (this) {if (!changed) // 如果没有变化,直接返回return;arrLocal = obs.toArray(new Observer[obs.size()]);clearChanged(); // 重置变化标志}// 遍历观察者列表,调用 update 方法for (Observer observer : arrLocal) {observer.update(this, arg);}}// 删除所有观察者public synchronized void deleteObservers() {obs.removeAllElements();}// 标记对象已改变protected synchronized void setChanged() {changed = true;}// 重置变化标志protected synchronized void clearChanged() {changed = false;}// 检查对象是否已改变public synchronized boolean hasChanged() {return changed;}// 返回观察者数量public synchronized int countObservers() {return obs.size();}
}

Spring 框架中的事件机制

Spring 框架中的事件机制是基于观察者模式实现的。它允许开发者定义自定义事件,并通过监听器(观察者)来处理这些事件。

Spring事件机制的使用

自定义事件

class CustomEvent extends ApplicationEvent {private String message;public CustomEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}

事件监听器(观察者)

@Component
class CustomEventListener implements ApplicationListener<CustomEvent> {@Overridepublic void onApplicationEvent(CustomEvent event) {System.out.println("收到事件: " + event.getMessage());}
}

事件发布者

@Component
class CustomEventPublisher {private final AnnotationConfigApplicationContext context;public CustomEventPublisher(AnnotationConfigApplicationContext context) {this.context = context;}public void publishEvent(String message) {context.publishEvent(new CustomEvent(this, message));}
}

配置类

@Configuration
@ComponentScan
class AppConfig {}

测试类

public class SpringEventDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);CustomEventPublisher publisher = context.getBean(CustomEventPublisher.class);publisher.publishEvent("Hello, Spring Event!");context.close();}
}

输出内容

收到事件: Hello, Spring Event!

Spring的事件机质的源码实现

Spring 事件机制的核心组件包括:

  • ApplicationEvent:事件的基类,所有自定义事件都需要继承它。对应观察者模式中的“事件”。
  • ApplicationListener:观察者接口,定义了处理事件的方法。对应观察者模式中的“观察者”。
  • ApplicationEventPublisher:事件发布者接口,用于发布事件。对应观察者模式中的“主题”。
  • ApplicationEventMulticaster:事件广播器,负责将事件分发给所有监听器。类似于观察者模式中的“通知机制”。

ApplicationEventApplicationEvent 是所有事件的基类,它继承自 java.util.EventObject

public abstract class ApplicationEvent extends EventObject {private final long timestamp;            // timestamp: 事件发生的时间戳。public ApplicationEvent(Object source) { // source:事件源,通常是发布事件的对象。super(source);this.timestamp = System.currentTimeMillis();}public final long getTimestamp() {return this.timestamp;}
}

ApplicationListener接口: ApplicationListener是观察者接口,定义了处理事件的方法。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);  // 当事件发生时,会调用此方法。
}

ApplicationEventPublisher接口: 是事件发布者接口,用于发布事件。

@FunctionalInterface
public interface ApplicationEventPublisher {default void publishEvent(ApplicationEvent event) {publishEvent((Object) event);}void publishEvent(Object event);
}

ApplicationEventMulticaster接口:是事件广播器接口,负责将事件分发给所有监听器。

public interface ApplicationEventMulticaster {void addApplicationListener(ApplicationListener<?> listener);void addApplicationListenerBean(String listenerBeanName);void removeApplicationListener(ApplicationListener<?> listener);void removeApplicationListenerBean(String listenerBeanName);void removeAllListeners();void multicastEvent(ApplicationEvent event); void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}

SimpleApplicationEventMulticasterSimpleApplicationEventMulticasterApplicationEventMulticaster的默认实现类。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {// 遍历所有监听器,并调用 onApplicationEvent 方法。@Overridepublic void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor();if (executor != null) {executor.execute(() -> invokeListener(listener, event));} else {invokeListener(listener, event);}}}// 实际调用监听器的 onApplicationEvent 方法。protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);} catch (ClassCastException ex) {// 处理类型转换异常}}
}

三、总结

观察者模式的优点

解耦:主题(Subject)和观察者(Observer)之间是松耦合的(主题不需要知道观察者的具体实现,只需要知道观察者接口,观察者也不需要知道主题的具体实现,只需要实现观察者接口)
动态管理依赖:观察者可以动态注册和注销,而不需要修改主题的代码。支持运行时动态添加或移除观察者,灵活性高。
符合开闭原则:可以轻松添加新的观察者,而不需要修改主题的代码。主题的代码不需要因为观察者的变化而修改。
广播通信:主题可以一次性通知所有观察者,适合一对多的通信场景。观察者可以根据需要选择是否响应通知。
职责分离:主题负责维护状态和通知观察者。观察者负责处理状态变化的逻辑。职责分离使得代码更加清晰和可维护。

观察者模式的缺点

  • 性能问题
    • 如果观察者数量非常多,通知所有观察者可能会消耗大量时间。
    • 如果观察者的处理逻辑复杂,可能会导致性能瓶颈。
  • 内存泄漏
    • 如果观察者没有正确注销,可能会导致观察者无法被垃圾回收,从而引发内存泄漏。
    • 特别是在长时间运行的应用中,需要特别注意观察者的生命周期管理。
  • 调试困难
    • 由于观察者和主题是松耦合的,调试时可能难以追踪事件的传递路径。
    • 如果观察者的处理逻辑出现问题,可能不容易定位问题根源。
  • 事件顺序不确定
    • 观察者收到通知的顺序通常是不确定的,如果业务逻辑对顺序有要求,可能需要额外的处理。

观察者模式的适用场景

  • 事件驱动系统
    • 当一个对象的状态变化需要触发其他对象的操作时,可以使用观察者模式。
    • 例如:GUI 框架中的按钮点击事件、Spring 框架中的事件机制。
  • 一对多的依赖关系
    • 当一个对象的状态变化需要通知多个其他对象时,可以使用观察者模式。
    • 例如:气象站和多个显示设备的关系。
  • 跨系统的消息通知
    • 在分布式系统中,观察者模式可以用于实现消息的发布和订阅。
    • 例如:消息队列(MQ)中的发布-订阅模型。
  • 状态变化的广播
    • 当一个对象的状态变化需要广播给多个对象时,可以使用观察者模式。
    • 例如:游戏中的角色状态变化通知其他系统(如 UI、音效等)。
  • 解耦业务逻辑
    • 当需要将业务逻辑解耦为多个独立的模块时,可以使用观察者模式。
    • 例如:订单系统中的订单状态变化通知库存系统、物流系统等。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

相关文章:

【设计模式】【行为型模式】观察者模式(Observer)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…...

RAGFlow和Dify对比

‌ RAGFlow和Dify都是基于大语言模型&#xff08;LLM&#xff09;的应用开发平台&#xff0c;具有相似的功能和应用场景&#xff0c;但它们在技术架构、部署要求和用户体验上存在一些差异。‌‌ RAGFlow和Dify对比 2025-02-13 22.08 RAGFlow‌ ‌技术栈‌&#xff1a;RAGFlow…...

AI前端开发:蓬勃发展的机遇与挑战

人工智能&#xff08;AI&#xff09;领域的飞速发展&#xff0c;正深刻地改变着我们的生活方式&#xff0c;也为技术人才&#xff0c;特别是AI代码生成领域的专业人士&#xff0c;带来了前所未有的机遇。而作为AI应用与用户之间桥梁的前端开发&#xff0c;其重要性更是日益凸显…...

结构型模式---代理模式

概念 代理模式是一种结构型模式&#xff0c;主要用于在客户端和接口之间添加一个中间层&#xff0c;用于在客户端和接口之间进行权限控制或者其他的中间层操作。 使用场景 1、延缓初始化&#xff0c;当我们偶尔需要使用一个重量级的服务对象&#xff0c;如果一直保持该对象的…...

Java面向对象一:相关概念

面向过程&面向对象 面向过程思想 步骤清晰简单&#xff0c;第一步做什么&#xff0c;第二步做什么… 面对过程适合处理一些较为简单的问题面向对象思想 物以类聚&#xff0c;分类的思维模式&#xff0c;思考问题首先会解决问题需要哪些分类&#xff0c;然后对这些分类进行…...

CEF132 编译指南 MacOS 篇 - depot_tools 安装与配置 (四)

1. 引言 在 CEF132&#xff08;Chromium Embedded Framework&#xff09;的编译过程中&#xff0c;depot_tools 扮演着举足轻重的角色。这套由 Chromium 项目精心打造的脚本和工具集&#xff0c;专门用于获取、管理和更新 Chromium 及其相关项目&#xff08;包括 CEF&#xff…...

React VS Vue

React 和 Vue 是目前最流行的两个前端框架&#xff0c;它们在设计理念、生态系统和开发体验上各有特点。以下是对 React 和 Vue 的全方位对比&#xff1a; 1. 核心设计理念 React 库而非框架&#xff1a;React 是一个用于构建 UI 的库&#xff0c;专注于视图层&#xff0c;其…...

伺服报警的含义

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在开发C#的运动控制程序的时候&#xff0c;一个必要的步骤就是设置伺服报警信号的…...

CSS 属性选择器详解与实战示例

CSS 属性选择器是 CSS 中非常强大且灵活的一类选择器&#xff0c;它能够根据 HTML 元素的属性和值来进行精准选中。在实际开发过程中&#xff0c;属性选择器不仅可以提高代码的可维护性&#xff0c;而且能够大大优化页面的样式控制。本文将结合菜鸟教程的示例&#xff0c;从基础…...

基于STM32、HAL库、HS12864(ST7920,并行接口)C语言程序设计

1、hs12864.h头文件: #ifndef __HS12864_H #define __HS12864_H #ifdef __cplusplus extern "C" {#endif #include "stm32l4xx_hal.h" // 控制线定义 - 根据实际硬件修改 #define HS12864_RS_GPIO_PORT GPIOC #define HS12864_RS_PIN GPIO_PI…...

Python练习11-20

题目&#xff1a;古典问题&#xff1a;有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; 题目&#xff1a;判断101-200之间有多少…...

探索ELK 的魅力

在大数据时代&#xff0c;海量日志和数据的收集、存储、处理与可视化分析变得越来越重要。而 ELK 堆栈&#xff0c;由 Elasticsearch、Logstash、Beats 和 Kibana 组成&#xff0c;正是一个强大的开源解决方案&#xff0c;帮助开发者和运维人员高效管理和分析日志数据。本文将详…...

【ROS2综合案例】乌龟跟随

一、前期准备 1.1 安装 1. 首先安装“乌龟跟随”案例的功能包以及依赖项。 安装方式1&#xff08;二进制方式安装&#xff09;&#xff1a; sudo apt-get install ros-humble-turtle-tf2-py ros-humble-tf2-tools ros-humble-tf-transformations 安装方式2&#xff08;克…...

多式联运最优路径算法

多式联运的最优路径优化问题涉及运输成本、时间、碳排放等多目标权衡&#xff0c;需结合运输方式&#xff08;公路、铁路、水路、航空等&#xff09;的协同性&#xff0c;通过算法模型寻找综合最优解。以下是相关研究进展与算法应用的总结&#xff1a; 一、多式联运路径优化的核…...

GPT-SWARM和AgentVerse的拓扑结构和交互机制

GPT-SWARM和AgentVerse的拓扑结构和交互机制 拓扑结构区别 GPT-SWARM:采用图结构,将语言智能体系统描述为可优化的计算图。图中的每个节点代表一个操作,如语言模型推理或工具使用等特定功能,边则描述了操作之间的信息流,代表智能体之间的通信渠道。多个智能体连接形成的复…...

信号检测和信道均衡的联系

1. 系统模型 假设一个通信系统的数学模型如下&#xff1a; 发送信号&#xff1a; s [ s 1 , s 2 , … , s N ] T \mathbf{s} [s_1, s_2, \dots, s_N]^T s[s1​,s2​,…,sN​]T&#xff0c;其中 s i s_i si​ 是发送符号。信道矩阵&#xff1a; H \mathbf{H} H&#xff08;…...

优化线程池关闭机制以避免无限循环

引言 在多线程编程中&#xff0c;正确关闭线程池是一个重要的任务&#xff0c;以确保程序的稳定性和资源的有效利用。本文将探讨一种常见的线程池关闭机制&#xff0c;并提出优化建议&#xff0c;以避免无限循环和资源浪费。 问题描述 在实际开发中&#xff0c;我们经常使用…...

持久性HTTPVS.非持久性HTTP

1. HTTP协议基础 HTTP&#xff08;HyperText Transfer Protocol&#xff09;是Web通信的核心协议&#xff0c;定义了客户端&#xff08;浏览器&#xff09;与服务器之间传输数据的规则。 在HTTP/1.0及之前的版本中&#xff0c;默认使用非持久性连接&#xff0c;而HTTP/1.1及更…...

自动化UI测试 | 什么是测试驱动开发(TDD)和行为驱动开发(BDD)?有何区别?

TDD&#xff08;测试驱动开发&#xff09;和BDD&#xff08;行为驱动开发&#xff09;是两种独特的软件开发技术&#xff0c;它们在测试的内容和方式上有所不同。尽管名称相似&#xff0c;但服务于不同的目的。 什么是TDD&#xff1f; TDD代表测试驱动开发。它是一个过程&…...

在 PyCharm 中接入deepseek的API的各种方法

在 PyCharm 中接入 DeepSeek 的 API&#xff0c;通常需要以下步骤&#xff1a; 1. 获取 DeepSeek API 密钥 首先&#xff0c;确保你已经在 DeepSeek 平台上注册并获取了 API 密钥&#xff08;API Key&#xff09;。如果没有&#xff0c;请访问 DeepSeek 的官方网站注册并申请 …...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Yolov8 目标检测蒸馏学习记录

yolov8系列模型蒸馏基本流程&#xff0c;代码下载&#xff1a;这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中&#xff0c;**知识蒸馏&#xff08;Knowledge Distillation&#xff09;**被广泛应用&#xff0c;作为提升模型…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...