观察者模式实战:从Java代码到事件驱动架构的完整指南
1. 为什么说观察者模式是发布订阅思想的最朴素实现先讲个我自己的经历。几年前我接手一个内部工单系统用户提了一个需求管理员审核通过某个申请后需要同时触发邮件通知、短信提醒、OA流程归档、数据报表更新四个动作。第一版代码很直接在审核通过的业务方法里依次调用这四个模块的方法。结果上线两周就出事了——新增了一个企业微信通知需求负责的老同事直接在审核方法里加了第五个调用。又过了一个月产品要求把邮件通知改成异步发送于是审核方法里又开始调异步线程池。等到这个类被改到七八百行的时候所有人都在心里骂这该死的耦合。这就是观察者模式要解决的问题。它的核心思想其实非常朴素当一个对象的状态发生变化时所有依赖它的对象都能得到通知并自动更新。用更接地气的话说就是我发出了一条广播谁关心谁自己来听而不是我挨个打电话告诉每个人。在这个设计模式里有两个核心角色被观察者Subject维护一个观察者列表提供注册和移除观察者的方法状态变化时负责通知所有注册的观察者。观察者Observer提供一个更新接口在被观察者通知时执行具体的响应逻辑。你可能觉得这不就是事件监听吗对事件监听就是观察者模式的经典应用。Java里的java.util.Observer和java.util.Observable就是JDK自带的观察者模式实现GUI编程里的按钮点击监听器前端框架里的数据双向绑定底层的思路全都是这一套。刚接触设计模式的人容易犯一个毛病就是把观察者模式和发布订阅模式画等号。严格来说这两者是有区别的。经典的观察者模式中被观察者直接持有观察者的引用通知是同步、直接调用观察者接口方法的。而发布订阅模式通常在两者中间加一层事件通道Event Channel发送方和接收方互相不认识完全通过中间件解耦。像我们常用的消息队列Kafka、RabbitMQ就是典型的发布订阅而观察者模式更多用于同一进程内的对象间通信。对于从零开始学设计模式的人来说观察者模式应该排在行为型模式里比较靠前的位置去学原因有三个。第一它的概念非常直观几乎不需要什么抽象思维就能理解第二它是理解松耦合这个设计原则的最好教材一个“订阅列表”就把发送方和接收方拆开了第三它的变体和扩展极多学完基础的观察者模式之后再去看事件驱动架构、响应式编程、MVC里的Model和View交互都能找到同一套思想的影子。这篇内容就带你从定义到实现再到实战中那些文档里不会写的坑完整地把观察者模式讲透。全程用代码说话用场景理解不讲玄学。2. 先搞清楚被观察者、观察者与依赖倒置的关系很多人看UML图看得很晕什么接口、聚合、箭头记不住。我建议你换个方法别看类图先把这三个角色当成现实生活中的人来理解。想象一个小区业主群。物业被观察者发布停水通知业主观察者们不需要整天打电话问物业停水了没只需要在群里等着通知。业主想退群就退群物业也不需要记录哪个业主一定要收到通知——它只管在群里喊一嗓子。对应到代码层面就是三件事被观察者持有一个观察者列表提供addObserver/removeObserver方法。被观察者在状态变化时遍历列表调用每个观察者的update方法。观察者通过实现统一的接口让自己可以被被观察者回调。这里有一个关键的设计决策观察者接口一定是被观察者依赖的抽象而不是具体的实现类。如果被观察者直接持有具体观察者类的引用比如在代码里写new EmailSender().send()那就回到了我开头说的那种耦合状态——每新增一个观察者就得改被观察者类的代码。而面向接口编程之后新增观察者只需要实现接口并注册进来被观察者的代码可以完全不动。这个点用术语说就是依赖倒置原则的落地高层模块被观察者的通知逻辑不依赖低层模块具体观察者两者都依赖于抽象观察者接口。还有一点容易被忽略就是通知的方向。观察者模式里数据流是单向的被观察者 → 观察者。观察者不能反向去修改被观察者的状态否则就会出现你通知我我又改你你又通知我的循环调用。实际项目里我见过一个死循环事故就是A监听BB监听A双方状态一变化就互相触发最后栈溢出。这是后话先记住数据流单向是观察者模式的纪律。那什么情况下该用观察者模式我自己的判断标准有三个一个对象的变化需要触发多个其他对象的联动行为联动对象的数量和类型经常变化甚至可能在运行时动态增减触发方不关心谁在处理自己的状态变化也不想处理处理结果。反过来如果只有一个下游模块且这个下游模块和触发方生命周期完全一致那直接调用就行没必要硬套模式。设计模式是解决特定问题的工具不是装饰代码的花瓶。3. 从零手写观察者模式一个完整可运行的Java示例概念说再多不如亲手写一遍。我用Java来实现一个非常经典的场景天气预报站发布天气数据两个展示面板手机App、网页实时更新显示。这个例子麻雀虽小但把观察者模式的完整流程都走了一遍。3.1 定义观察者接口观察者接口只需要一个方法用来接收被观察者的通知。这里我习惯把数据一起传过去也就是后面会提到的推模型public interface Observer { void update(float temperature, float humidity, float pressure); }3.2 定义被观察者接口被观察者接口负责管理观察者的注册和移除public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); }3.3 实现具体的被观察者被观察者持有观察者列表并在天气数据变化时遍历通知import java.util.ArrayList; import java.util.List; public class WeatherData implements Subject { private final ListObserver observers new ArrayList(); private float temperature; private float humidity; private float pressure; Override public void registerObserver(Observer observer) { if (observer null) { return; } if (!observers.contains(observer)) { observers.add(observer); } } Override public void removeObserver(Observer observer) { observers.remove(observer); } Override public void notifyObservers() { for (Observer observer : observers) { observer.update(temperature, humidity, pressure); } } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature temperature; this.humidity humidity; this.pressure pressure; notifyObservers(); } }setMeasurements方法模拟气象站采集到新数据调用notifyObservers触发所有人的更新。3.4 实现具体观察者public class PhoneDisplay implements Observer { Override public void update(float temperature, float humidity, float pressure) { System.out.println([手机App] 当前温度: temperature °C, 湿度: humidity %, 气压: pressure hPa); } } public class WebDisplay implements Observer { Override public void update(float temperature, float humidity, float pressure) { System.out.println([网页端] 温度: temperature °C, 湿度: humidity %, 气压: pressure hPa); } }3.5 跑起来看效果public class WeatherStation { public static void main(String[] args) { WeatherData weatherData new WeatherData(); PhoneDisplay phoneDisplay new PhoneDisplay(); WebDisplay webDisplay new WebDisplay(); weatherData.registerObserver(phoneDisplay); weatherData.registerObserver(webDisplay); weatherData.setMeasurements(25.3f, 65.0f, 1013.0f); System.out.println(---- 网页端退订只保留手机端通知 ----); weatherData.removeObserver(webDisplay); weatherData.setMeasurements(26.1f, 63.0f, 1012.5f); } }运行结果[手机App] 当前温度: 25.3°C, 湿度: 65.0%, 气压: 1013.0hPa [网页端] 温度: 25.3°C, 湿度: 65.0%, 气压: 1013.0hPa ---- 网页端退订只保留手机端通知 ---- [手机App] 当前温度: 26.1°C, 湿度: 63.0%, 气压: 1012.5hPa整个流程非常清晰。第一次发布数据两个观察者都收到了通知网页端退订之后第二次发布数据就只有手机端响应了。注意removeObserver之后被观察者的通知代码一行都没改——这就是面向接口编程带来的好处。我在写这个例子的时候有意识地处理了重复注册的问题。registerObserver里用contains判断了一遍防止同一个观察者被注册两次导致重复通知。这是个很小的细节但在真实项目里重复注册引起的同一封邮件发两遍问题我确实碰到过原因就是调用方在不同地方不小心注册了多次。3.6 推模型与拉模型的选择上面的写法属于推模型被观察者把数据作为参数传给观察者。还有一种拉模型被观察者只通知状态变了由观察者自己通过传入的被观察者引用来获取需要的数据。两种方式没有绝对的好坏我总结了一个对比表供你参考维度推模型拉模型通知参数将被观察者完整数据传给观察者只传被观察者自身观察者主动获取耦合程度观察者依赖数据结构数据变化时同步修改所有观察者签名观察者依赖被观察者接口相对更稳定灵活性差参数有变化就要改接口好观察者只取自己关心的数据适用场景所有观察者需要的数据基本一致各观察者关心的数据差异较大JDK 1.1 之后Java官方推荐Observer.update(Observable o, Object arg)这种传被观察者和附加参数的写法本质上是一种推拉结合。一种折中做法是把数据封装成一个Message对象观察者接口只接收这一个对象。这样新增字段时不用改方法签名坏的只是Message内部的属性比直接改参数列表要温和得多。我在实际项目里就偏向这种封装做法后面会细说。4. 观察者模式在你天天用的框架里到底长什么样有些读者学设计模式有一个困惑我在代码里好像从没见过这个模式学了有什么用实际上观察者模式是所有行为型模式里应用最广的只是你在用的时候没意识到自己已经在用了。我挑几个典型场景说说你会发现这模式无处不在。4.1 Java GUI事件监听按钮点击的底层逻辑在 Swing 或 AWT 里写一个按钮的点击事件JButton button new JButton(点我); button.addActionListener(e - System.out.println(按钮被点击了));button就是被观察者ActionListener就是观察者接口addActionListener就是注册观察者的方法。点击按钮时Swing内部会调用notifyObservers去通知所有注册的监听器。你写了几年的onClick回调其实就是在用观察者模式。理解了这层你再看 Android 的setOnClickListener、前端的addEventListener、Qt 的信号槽机制会发现底层思路全是同一个。4.2 Vue响应式系统数据变化驱动视图更新Vue 的响应式原理是面试高频题它的核心就是观察者模式加数据劫持。当你在data里声明message: hello时Vue 会为每个属性创建依赖收集器Dep每个使用到该属性的组件渲染函数Watcher就是观察者。当message改变时Dep 通知所有 Watcher 重新渲染。这里有个微妙的地方依赖是动态收集的。Vue 在首次渲染时才知道哪些组件用到了这个属性也就是说观察者的注册不是写死的而是在运行时由框架动态完成。这正是观察者模式里观察者列表是运行时可变的这个特性在真实项目里的放大版。4.3 游戏开发里的成就解锁与任务系统游戏开发是观察者模式的重灾区。以杀死一个Boss这个事件为例它可能触发经验值增加、成就解锁、掉落物生成、任务进度更新四件事。如果在战斗系统里直接写这四段逻辑那每加一个击杀后要触发的动作比如通关动画战斗系统代码就要被改一次。用观察者模式Boss死亡时只需要发一条我被杀了的事件经验系统、成就系统、任务系统各自作为观察者注册监听。后续新增击杀统计系统只需要在启动时注册战斗系统一行代码不用动。这套思路在Unity、Unreal的开发里非常常见很多人觉得事件系统是高深的东西其实底层原型就是观察者模式。4.4 消息中间件与发布订阅的进化如果你用过 Redis 的发布订阅或者 Kafka 的这种消息队列会发现它们的 API 设计很像观察者模式生产者发布消息到主题消费者订阅主题。区别在于经典观察者模式的被观察者直接调用观察者方法是进程内的同步调用而消息中间件里的被观察者其实是消息队列生产者和消费者完全隔离是跨进程的异步调用。从观察者模式到发布订阅模式本质上是把直接依赖替换成了中间介质。理解这条演进路线你就能理解为什么有人说观察者模式是事件驱动架构的基石。Java里还有一个很有参考价值的例子——PropertyChangeSupport。JDK自带的属性变更通知机制在bean的属性变化时触发PropertyChangeListener回调。用过这个类你就会发现JDK在很早就把观察者模式标准化了只是在Spring等框架普及之后大家更习惯用注解式的EventListener来做这件事。5. 实战用观察者模式重写一个订单状态通知系统光看实例不过瘾我把文章开头说的工单场景重构成观察者模式的代码整个过程走一遍这就是你下次可以直接抄作业的代码。5.1 老代码为什么让人崩溃先看看原始的耦合代码长什么样public class OrderService { public void approveOrder(String orderId) { // 业务逻辑 System.out.println(订单 orderId 审核通过); // 四个联动动作全部写死在这里 emailSender.sendEmail(orderId); smsSender.sendSms(orderId); oaArchiveService.archive(orderId); reportService.updateReport(orderId); } }这个approveOrder方法的问题闭着眼睛都能数出来每新增一个联动模块就要改approveOrder方法如果某个联动模块临时故障比如短信服务挂了可能影响整个订单审核流程各模块之间的启动顺序无法控制但需求一变就得改代码顺序单元测试时测试订单审核逻辑结果邮件短信全被触发。5.2 用观察者模式重构定义订单事件接口和监听器接口public interface OrderEvent { String getOrderId(); }public interface OrderEventListener { void onOrderApproved(OrderEvent event); }订单服务变成被观察者import java.util.ArrayList; import java.util.List; public class OrderService { private final ListOrderEventListener listeners new ArrayList(); public void addListener(OrderEventListener listener) { listeners.add(listener); } public void removeListener(OrderEventListener listener) { listeners.remove(listener); } public void approveOrder(String orderId) { // 核心业务逻辑 System.out.println(订单 orderId 审核通过); OrderEvent event () - orderId; // 通知所有监听者 for (OrderEventListener listener : listeners) { listener.onOrderApproved(event); } } }各个联动模块实现监听器接口public class EmailNotifier implements OrderEventListener { Override public void onOrderApproved(OrderEvent event) { System.out.println(发送邮件通知: 订单 event.getOrderId() 审核通过); } } public class SmsNotifier implements OrderEventListener { Override public void onOrderApproved(OrderEvent event) { System.out.println(发送短信通知: 订单 event.getOrderId() 审核通过); } }在系统初始化时注册监听器public class Application { public static void main(String[] args) { OrderService orderService new OrderService(); orderService.addListener(new EmailNotifier()); orderService.addListener(new SmsNotifier()); orderService.approveOrder(SO-2025-001); } }对比重构前和重构后的代码最明显的变化是OrderService不再认识EmailNotifier和SmsNotifier它只认识OrderEventListener接口。以后再加一个企业微信通知只需要写一个WechatNotifier implements OrderEventListener然后在Application里注册OrderService代码一个字都不用改。这就是开闭原则的体现——对扩展开放对修改封闭。5.3 为什么把事件本身也抽象成对象你可能注意到我写了一个OrderEvent接口而不是把orderId直接传给监听器。这是我从实战教训里学到的。第一次重构这个工单系统的时候我偷懒直接写void onOrderApproved(String orderId)。结果不到一个月产品说审核通过时需要带上审核人姓名我改监听器接口、改所有实现类、改OrderService的调用……光是编译错误就修了一下午。把OrderEvent抽象出来之后追加字段只需要改实现public class OrderApprovedEvent implements OrderEvent { private final String orderId; private final String approver; public OrderApprovedEvent(String orderId, String approver) { this.orderId orderId; this.approver approver; } Override public String getOrderId() { return orderId; } public String getApprover() { return approver; } }监听器接口不用变测试代码不用大改这就是事件对象封装的价值。在我的实践经验里事件类的设计有一个原则事件类只携带数据不包含业务逻辑。谁拿到这个事件怎么处理是监听器自己的事事件类自身不做任何判断。6. 使用观察者模式时最容易踩的四个坑多写几个真实项目你会发现观察者模式的实现本身不难难的是把它的副作用控制好。下面这四个问题是我在代码评审和线上故障里见到最多的。6.1 遍历通知时移除观察者抛出 ConcurrentModificationExceptionnotifyObservers用增强 for 或者迭代器遍历列表时如果某个观察者在自己的update方法里直接调用了被观察者的removeObserver会抛出ConcurrentModificationException。原因很简单foreach 遍历时不允许修改集合结构。解决办法是在副本上遍历或者在移除时做延迟移除public void notifyObservers() { ListObserver snapshot new ArrayList(observers); for (Observer observer : snapshot) { observer.update(...); } }这样即使观察者在更新时把自己移除了也只是影响observers原集合正在遍历的快照不受影响。6.2 一个观察者异常导致通知链路中断notifyObservers是同步调用如果某个观察者的update方法抛出异常后面的观察者全都会收不到通知。这是观察者模式最隐蔽的坑。我之前处理过一个线上事故订单审核通过后邮件通知模块的数据源临时不可用抛了个运行时异常结果后续的短信通知和OA归档全被连带跳过了。业务表现就是邮件没发、短信没发、OA没归档排查了很久才发现是通知链路上一个异常把全部下游带崩了。我的经验是在notifyObservers里对每个观察者做异常隔离public void notifyObservers() { for (Observer observer : observers) { try { observer.update(...); } catch (Exception e) { System.err.println(观察者执行失败: observer , e.getMessage()); // 记录日志继续通知下一个观察者 } } }基础的观察者模式教材不会讲这一点但真实项目中这条非常重要。就算你认为所有观察者都不会抛异常也建议加一层保护成本几乎为零收益却很大。6.3 观察者持有被观察者的引用内存泄漏风险如果观察者持有被观察者内部的大对象引用或者被观察者持有观察者的引用而两者生命周期又不同步就可能出现内存泄漏。例如一个界面组件注册了全局事件监听界面关闭了却忘记移除监听那么被观察者会一直持有这个界面组件的引用垃圾回收器永远无法回收它。解决办法只有一条在不需要监听时一定要调用removeObserver/removeListener。很多框架提供的onDestroy、dispose、cleanup生命周期方法就是干这个的。更进阶一点的写法是使用WeakReference包装观察者列表让垃圾回收器能回收不活跃的观察者对象。但这会带来另一个问题弱引用的通知顺序不稳定而且你可能需要手动清理空引用。我的建议是单一职责的生命周期管理优先弱引用作为补充手段别一上来就玩技巧。6.4 观察者的执行顺序不确定业务却依赖顺序默认情况下观察者的通知顺序就是注册顺序。但如果你依赖这个顺序做业务逻辑比如必须先归档OA再发邮件那么一旦有人调整注册顺序或者我上面加了异常隔离跳过了某个观察者你的顺序保证就没了。我遇到过这样一个需求订单审核后必须先把数据写入报表系统再发送通知邮件因为邮件里要带报表的统计数字。这种强顺序依赖不适合用观察者模式来做。如果你用了观察者模式又需要保证执行顺序有两个方案只发一个OrderApprovedEvent由一个调度观察者负责按顺序调用后续逻辑把多个动作合并成一个观察者在这个观察者内部维护顺序。从架构角度讲观察者之间应该是互相独立的如果它们之间有依赖那说明你的设计可能不太适合观察者模式。7. 观察者模式与发布订阅、中介者模式的边界在哪里学行为型模式的时候观察者、发布订阅、中介者这三个概念最容易混。我在这里帮你理清楚免得期末考试或者面试时一紧张就答串。7.1 观察者模式 vs 发布订阅模式前面说过Observer直接持有Observer引用通知是直接调用发布订阅模式中间多了一层事件通道。这带来三个实际差异对比维度观察者模式发布订阅模式通信双方被观察者知道观察者是谁发布方和订阅方互不认识耦合方式通过接口直接耦合通过事件通道完全解耦同步异步通常同步可以异步、跨进程典型实现Java Listener、Vue响应式Redis Pub/Sub、Kafka、RabbitMQ如果你在一个系统里只是想让几个内部模块联动观察者模式足够如果你需要把消息发给其他服务或者追求极致的解耦和扩展那就用消息队列。7.2 观察者模式 vs 中介者模式中介者模式解决的是多个对象互相引用形成网状结构的问题。它的思路是引入一个中介对象让所有对象都只依赖中介把网状结构变成星状结构。观察者模式和中介者模式的区别在哪观察者模式解决的是一对多通知中介者模式解决的是多对多交互。打个比方观察者模式就像物业在业主群里发通知业主之间不用互相沟通中介者模式就像机场塔台所有飞机都跟塔台对话不直接跟其他飞机交流。如果你发现观察者列表里的对象之间开始互相调用了说明你的观察者之间出现了依赖这时候要么拆分成多个观察者事件要么考虑用中介者模式来管控它们之间复杂的交互。7.3 观察者模式的变体事件总线实际项目中观察者模式最常见的变体就是事件总线Event Bus。它把观察者模式中的被观察者抽象成总线对象任何对象都可以向总线发事件任何对象都可以注册监听某个类型的事件。EventBus bus new EventBus(); bus.register(OrderApprovedEvent.class, new EmailNotifier()); bus.post(new OrderApprovedEvent(SO-2025-001, 张三));Guava提供的EventBus或者自己手写一个简单的总线本质上就是观察者模式的中心化版本。当你觉得被观察者要维护一个观察者列表太麻烦时事件总线可以让任何类不需要继承或实现Subject接口就能发事件。不过要注意事件总线用多了代码里的事件流会变得难以追踪——你只知道发了一个事件但不清楚谁能收到。所以在引入事件总线之前先想清楚你的项目真的需要这种全局解耦吗如果你只是在一个模块内部做通知用最朴素的观察者模式就行没必要把架构搞复杂。8. 到底要不要用Jdk自带的Observer和Observable很多教材会教你直接用java.util.Observable和java.util.Observer因为代码量最少。我在学习阶段也这么干过但实际项目里我从来不推荐这么做。为什么有两个硬伤。第一Observable是一个类而不是接口Java单继承的机制决定了你的业务类没法再继承其他类。第二观察者模式的核心诉求是变化独立但Observable把API固定死了setChanged()、notifyObservers()这些方法的设计灵活性不足而且在Java 9之后这两个类已经被标记为过时deprecated了。还有一个很实际的问题——Observable的通知顺序不保证。如果你依赖注册顺序来完成业务JDK原生实现帮不了你。我的经验是自己定义一个观察者接口和对应的通知方法在没有任何框架约束的情况下永远是首选。代码量多的那几行换来的是完全的控制权通知顺序、异常隔离、线程模型、事件对象设计全都自己说了算。如果用Spring开发还可以直接用ApplicationEventPublisher和EventListener做事件发布和监听那是Spring对观察者模式的框架级实现。注意它是同步的如果要用异步需要配合Async使用。9. 写在最后我从观察者模式里学到的三件事这一篇写了这么多最后说点核心体会也是很多年前一位前辈教我的三句话我至今受用。第一当你发现代码里频繁出现新增一个功能就要打开原来的类修改的情况停下来想想能不能用观察者模式。不是要把所有联动都做成观察者而是在那些未来很可能会变的联动点上有意识地做解耦。第二设计模式的实现细节可以千变万化但意图是不变的。观察者模式的核心意图就是定义对象间一对多的依赖关系让多个观察者同时监听一个被观察者的状态变化。你可以用接口实现、用注解实现、用消息队列实现但只要抓住这个意图就不会被各种变体绕晕。第三学习设计模式的最好路径是先手写一遍再到框架源码里找实例最后在项目里尝试使用。只看不写永远是纸上谈兵只写不用很快就忘。观察者模式是所有模式里最好上手的一个花一早上手写一遍代码再去看Spring或Vue的源码你会发现世界突然通透了。下次写业务代码时如果你真的把一个审核通过的硬编码调用链改成了观察者模式那种四两拨千斤的感觉就是设计模式带给你的长期回报。

相关新闻

WeChatMsg 完整上手指南:3 种格式导出微信聊天记录,数据永久留在自己手里

WeChatMsg 完整上手指南:3 种格式导出微信聊天记录,数据永久留在自己手里

WeChatMsg 完整上手指南:3 种格式导出微信聊天记录,数据永久留在自己手里 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode…

2026/10/11 5:08:33 阅读更多 →
Python(Windows + Anaconda + VSCode)+Streamlit 简单学生成绩统计可视化看板(数据可视化)

Python(Windows + Anaconda + VSCode)+Streamlit 简单学生成绩统计可视化看板(数据可视化)

一、简介使用 Python、Pandas、Streamlit、Plotly 实现简单学生成绩统计静态数据看板。 通过代码自动生成模拟学生成绩数据集,完成数据清洗、指标统计、多类型图表可视化,最终在浏览器中展示完整可视化看板二、工程结构l/ ├─ main.py # 主程序…

2026/10/11 5:08:33 阅读更多 →
第2章:网络攻击原理与常用方法

第2章:网络攻击原理与常用方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 5:08:33 阅读更多 →

最新新闻

N皇后II深度解析:回溯算法与位运算优化实战

N皇后II深度解析:回溯算法与位运算优化实战

1. 题目拆解与整体设计思路1.1 题面内核:52 和 51 到底差在哪LeetCode 上 N 皇后系列有两道招牌题,51 题要求返回所有合法摆放方案的具体棋盘,52 题只要求返回方案总数。很多人在刷题指南里看到 N 皇后 II 的第一反应是“先做 51,…

2026/10/11 9:00:44 阅读更多 →
源码阅读方法论:如何高效读懂一个陌生项目

源码阅读方法论:如何高效读懂一个陌生项目

从"rea"这个命名聊起,我其实想写的是另一个词:read。有一次我在终端里敲命令,手一快把read打成了rea,回车之后才意识到拼写错了。但盯着这六个字符看了一会儿,我忽然觉得这恰好是一个很好的隐喻——很多开发…

2026/10/11 9:00:44 阅读更多 →
个人 NAS 入门避坑,普通人要不要自建存储?

个人 NAS 入门避坑,普通人要不要自建存储?

很多人看到 NAS 可以自动备份照片、存电影、远程访问,就心动入手。但 NAS 并不是人人都适合,先分清需求,再决定要不要上车,避免买回来吃灰。 ✅ 适合搭建 NAS 的人群 手机、相机照片视频量大,想要自动备份,…

2026/10/11 9:00:44 阅读更多 →
冷链车双司机换班,交接要对清哪些东西才不断档?

冷链车双司机换班,交接要对清哪些东西才不断档?

冷链车双司机换班,交接要对清哪些东西才不断档?长途冷链车配两个司机,本来是为了人停车不停、赶时效又保安全。但很多温度纠纷恰恰出在换班这个空档——交班的以为接班的看过温度,接班的以为上一班把机组弄好了,结果谁…

2026/10/11 9:00:44 阅读更多 →
南京资质齐全的定制商务车商家挑选全攻略

南京资质齐全的定制商务车商家挑选全攻略

商务车定制市场近年来持续升温,无论是企业商务接待、家庭多孩出行,还是高级会务保障,定制商务车都成了越来越多用户的。但市场繁荣的背后,选择门槛也在悄然提高:部分商家缺乏正规资质,定制方案还原度低&…

2026/10/11 9:00:44 阅读更多 →
SpringBoot+Vue在线教育后台管理系统:数据库设计到前后端分离实战

SpringBoot+Vue在线教育后台管理系统:数据库设计到前后端分离实战

做了这么多年后台管理系统,我越来越觉得所谓"设计和实现"这两件事是真正拉开差距的地方:设计没想清楚,代码写多少返工多少。前阵子帮一家职业技能培训机构整理在线教育管理后台时,我的第一反应不是先写接口,…

2026/10/11 8:59:44 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →