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

【从零入门23种设计模式20】行为型之状态模式

一、状态模式核心定义

状态模式是行为型设计模式的一种,核心目的是:

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

简单来说:将对象的每一种状态封装成独立的类,对象的行为由当前状态决定;状态切换时,对象会委托给对应状态类处理,而非通过大量 if/else 或 switch 判断

核心解决的问题
  1. 消除状态判断分支:避免在一个类中用大量 if/else/switch 判断不同状态的行为(如订单类中判断 “已创建 / 已支付 / 已发货 / 已取消” 的逻辑);
  2. 状态行为封装:每种状态的行为封装到独立类中,符合单一职责原则;
  3. 状态切换可控:状态的转换规则集中管理,可灵活扩展新状态;
  4. 行为与状态解耦:对象无需知道状态的具体实现,只需委托状态类处理行为。
生活类比
  • 场景 1:电梯运行状态
    • 环境类(Context):电梯本身;
    • 状态类:停止状态、运行状态、开门状态、关门状态;
    • 核心:电梯的行为(开门、关门、上行、下行)由当前状态决定 —— 开门状态下无法上行,停止状态下才能开门,运行状态下只能下行 / 上行。
  • 场景 2:订单状态流转
    • 环境类:订单对象;
    • 状态类:已创建、已支付、已发货、已收货、已取消;
    • 核心:订单的行为(支付、发货、取消)由当前状态决定 —— 已创建状态可支付,已支付状态可发货 / 取消,已发货状态不可取消。
  • 场景 3:游戏角色状态
    • 环境类:游戏角色;
    • 状态类:正常状态、攻击状态、防御状态、死亡状态;
    • 核心:角色的行为(攻击、防御、移动)由当前状态决定 —— 死亡状态下无法攻击 / 移动,防御状态下伤害减免。
标准角色
角色职责类比(订单场景)代码定位
环境类(Context)维护当前状态,委托当前状态处理行为;提供状态切换的入口订单对象(持有当前状态,调用状态行为)业务核心对象(如OrderElevator
抽象状态(State)定义所有状态的通用行为接口(如订单的支付、取消、发货方法)订单状态接口(pay ()、cancel ()、ship ())接口 / 抽象类
具体状态(ConcreteState)实现抽象状态接口,封装对应状态的行为;定义状态转换规则(如已创建状态支付后切换为已支付状态)已创建状态、已支付状态、已取消状态各状态的实现类
核心 UML 类图

二、订单状态流转

以 “订单状态流转” 为例,实现状态模式的核心逻辑 —— 覆盖所有核心角色,消除 if/else 判断,通过状态类封装不同状态的行为。

1. 步骤 1:定义抽象状态(订单状态接口)
/** * 抽象状态:订单状态接口(定义所有状态的通用行为) */ public interface OrderState { /** * 支付订单 */ void pay(OrderContext context); /** * 取消订单 */ void cancel(OrderContext context); /** * 发货订单 */ void ship(OrderContext context); /** * 获取当前状态名称 */ String getStateName(); }
2. 步骤 2:实现具体状态(各订单状态)
/** * 具体状态1:已创建状态(初始状态) */ public class CreatedState implements OrderState { @Override public void pay(OrderContext context) { // 已创建状态可支付,支付后切换为已支付状态 System.out.println("【已创建状态】订单支付成功,状态切换为已支付"); context.setState(new PaidState()); } @Override public void cancel(OrderContext context) { // 已创建状态可取消,取消后切换为已取消状态 System.out.println("【已创建状态】订单取消成功,状态切换为已取消"); context.setState(new CancelledState()); } @Override public void ship(OrderContext context) { // 已创建状态不可发货 System.out.println("【已创建状态】订单未支付,无法发货"); } @Override public String getStateName() { return "已创建"; } } /** * 具体状态2:已支付状态 */ public class PaidState implements OrderState { @Override public void pay(OrderContext context) { // 已支付状态不可重复支付 System.out.println("【已支付状态】订单已支付,无需重复支付"); } @Override public void cancel(OrderContext context) { // 已支付状态可取消,取消后切换为已取消状态 System.out.println("【已支付状态】订单取消成功,状态切换为已取消"); context.setState(new CancelledState()); } @Override public void ship(OrderContext context) { // 已支付状态可发货,发货后切换为已发货状态 System.out.println("【已支付状态】订单发货成功,状态切换为已发货"); context.setState(new ShippedState()); } @Override public String getStateName() { return "已支付"; } } /** * 具体状态3:已发货状态 */ public class ShippedState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("【已发货状态】订单已发货,无需支付"); } @Override public void cancel(OrderContext context) { System.out.println("【已发货状态】订单已发货,无法取消"); } @Override public void ship(OrderContext context) { System.out.println("【已发货状态】订单已发货,无需重复发货"); } @Override public String getStateName() { return "已发货"; } } /** * 具体状态4:已取消状态 */ public class CancelledState implements OrderState { @Override public void pay(OrderContext context) { System.out.println("【已取消状态】订单已取消,无法支付"); } @Override public void cancel(OrderContext context) { System.out.println("【已取消状态】订单已取消,无需重复取消"); } @Override public void ship(OrderContext context) { System.out.println("【已取消状态】订单已取消,无法发货"); } @Override public String getStateName() { return "已取消"; } }
3. 步骤 3:实现环境类(订单上下文)
/** * 环境类:订单上下文(维护当前状态,委托状态处理行为) */ public class OrderContext { // 订单ID private String orderId; // 当前状态(核心:委托该状态处理所有行为) private OrderState currentState; /** * 初始化订单,默认状态为已创建 */ public OrderContext(String orderId) { this.orderId = orderId; this.currentState = new CreatedState(); // 初始状态 System.out.println("【订单上下文】订单创建成功 | 订单ID:" + orderId + ",初始状态:" + currentState.getStateName()); } /** * 切换状态(供状态类调用) */ public void setState(OrderState state) { this.currentState = state; System.out.println("【订单上下文】订单状态更新 | 订单ID:" + orderId + ",当前状态:" + currentState.getStateName()); } /** * 支付订单(委托当前状态处理) */ public void pay() { System.out.println("\n【订单上下文】发起支付请求 | 订单ID:" + orderId); currentState.pay(this); } /** * 取消订单(委托当前状态处理) */ public void cancel() { System.out.println("\n【订单上下文】发起取消请求 | 订单ID:" + orderId); currentState.cancel(this); } /** * 发货订单(委托当前状态处理) */ public void ship() { System.out.println("\n【订单上下文】发起发货请求 | 订单ID:" + orderId); currentState.ship(this); } // Getter public String getOrderId() { return orderId; } public String getCurrentStateName() { return currentState.getStateName(); } }
4. 客户端(测试订单状态流转)
/** * 客户端:测试订单状态流转(状态模式) */ public class StateClient { public static void main(String[] args) { // 1. 创建订单上下文(初始状态:已创建) OrderContext order = new OrderContext("O20240314001"); // 2. 测试1:已创建状态下支付 order.pay(); // 3. 测试2:已支付状态下发货 order.ship(); // 4. 测试3:已发货状态下取消(不允许) order.cancel(); // 5. 重新创建订单,测试取消 System.out.println("\n======= 测试新订单取消 ======="); OrderContext order2 = new OrderContext("O20240314002"); order2.cancel(); // 6. 已取消状态下支付(不允许) order2.pay(); } }
输出结果
【订单上下文】订单创建成功 | 订单ID:O20240314001,初始状态:已创建 【订单上下文】发起支付请求 | 订单ID:O20240314001 【已创建状态】订单支付成功,状态切换为已支付 【订单上下文】订单状态更新 | 订单ID:O20240314001,当前状态:已支付 【订单上下文】发起发货请求 | 订单ID:O20240314001 【已支付状态】订单发货成功,状态切换为已发货 【订单上下文】订单状态更新 | 订单ID:O20240314001,当前状态:已发货 【订单上下文】发起取消请求 | 订单ID:O20240314001 【已发货状态】订单已发货,无法取消 ======= 测试新订单取消 ======= 【订单上下文】订单创建成功 | 订单ID:O20240314002,初始状态:已创建 【订单上下文】发起取消请求 | 订单ID:O20240314002 【已创建状态】订单取消成功,状态切换为已取消 【订单上下文】订单状态更新 | 订单ID:O20240314002,当前状态:已取消 【订单上下文】发起支付请求 | 订单ID:O20240314002 【已取消状态】订单已取消,无法支付

三、Spring 实战版(订单状态机 + 注解驱动)

在业务开发中,状态模式最核心的实战场景是结合 Spring 实现可配置的订单状态机—— 通过 Spring 的依赖注入管理状态类,通过注解简化状态切换,支持动态扩展新状态。

1. 依赖准备(Spring Boot)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>3.2.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- Spring状态机(可选,简化状态流转配置) --> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>4.0.1</version> </dependency> </dependencies>
2. 核心模型定义
import lombok.AllArgsConstructor; import lombok.Getter; /** * 订单状态枚举(统一状态标识) */ @Getter @AllArgsConstructor public enum OrderStatusEnum { CREATED("CREATED", "已创建"), PAID("PAID", "已支付"), SHIPPED("SHIPPED", "已发货"), RECEIVED("RECEIVED", "已收货"), CANCELLED("CANCELLED", "已取消"); private final String code; private final String desc; } /** * 订单事件枚举(触发状态切换的事件) */ @Getter @AllArgsConstructor public enum OrderEventEnum { PAY("PAY", "支付"), CANCEL("CANCEL", "取消"), SHIP("SHIP", "发货"), RECEIVE("RECEIVE", "确认收货"); private final String code; private final String desc; }
3. 抽象状态(Spring Bean 接口)
import org.springframework.stereotype.Component; /** * 抽象状态:订单状态接口(标记为Spring组件,便于注入) */ public interface OrderState { /** * 处理事件,返回新状态(核心:状态转换规则) */ OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event); /** * 获取当前状态枚举 */ OrderStatusEnum getStatus(); }
4. 具体状态(Spring Bean 实现)
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * 具体状态1:已创建状态(Spring Bean) */ @Slf4j @Component public class CreatedState implements OrderState { @Override public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) { switch (event) { case PAY: log.info("【已创建状态】处理支付事件 | 订单ID:{}", context.getOrderId()); return OrderStatusEnum.PAID; // 支付后切换为已支付 case CANCEL: log.info("【已创建状态】处理取消事件 | 订单ID:{}", context.getOrderId()); return OrderStatusEnum.CANCELLED; // 取消后切换为已取消 default: log.warn("【已创建状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc()); return context.getCurrentStatus(); // 不切换状态 } } @Override public OrderStatusEnum getStatus() { return OrderStatusEnum.CREATED; } } /** * 具体状态2:已支付状态(Spring Bean) */ @Slf4j @Component public class PaidState implements OrderState { @Override public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) { switch (event) { case SHIP: log.info("【已支付状态】处理发货事件 | 订单ID:{}", context.getOrderId()); return OrderStatusEnum.SHIPPED; // 发货后切换为已发货 case CANCEL: log.info("【已支付状态】处理取消事件 | 订单ID:{}", context.getOrderId()); return OrderStatusEnum.CANCELLED; // 取消后切换为已取消 default: log.warn("【已支付状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc()); return context.getCurrentStatus(); } } @Override public OrderStatusEnum getStatus() { return OrderStatusEnum.PAID; } } /** * 具体状态3:已发货状态(Spring Bean) */ @Slf4j @Component public class ShippedState implements OrderState { @Override public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) { switch (event) { case RECEIVE: log.info("【已发货状态】处理确认收货事件 | 订单ID:{}", context.getOrderId()); return OrderStatusEnum.RECEIVED; // 确认收货后切换为已收货 default: log.warn("【已发货状态】不支持事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc()); return context.getCurrentStatus(); } } @Override public OrderStatusEnum getStatus() { return OrderStatusEnum.SHIPPED; } } /** * 具体状态4:已取消状态(Spring Bean) */ @Slf4j @Component public class CancelledState implements OrderState { @Override public OrderStatusEnum handleEvent(OrderContext context, OrderEventEnum event) { log.warn("【已取消状态】不支持任何事件 | 订单ID:{},事件:{}", context.getOrderId(), event.getDesc()); return context.getCurrentStatus(); // 已取消状态不切换 } @Override public OrderStatusEnum getStatus() { return OrderStatusEnum.CANCELLED; } }
5. 环境类(Spring 管理的订单上下文)
import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 环境类:订单上下文(Spring Bean,管理状态映射和当前状态) */ @Data @Component public class OrderContext { private String orderId; private OrderStatusEnum currentStatus; // 状态映射:状态枚举 → 状态实例(Spring注入所有状态类) private final Map<OrderStatusEnum, OrderState> stateMap = new ConcurrentHashMap<>(); /** * 构造函数:注入所有OrderState实现类,初始化状态映射 */ @Autowired public OrderContext(Map<String, OrderState> stateBeans) { // 将Spring容器中的所有OrderState Bean放入映射 for (OrderState state : stateBeans.values()) { stateMap.put(state.getStatus(), state); } log.info("【订单上下文】初始化状态映射,共加载{}个状态类", stateMap.size()); } /** * 初始化订单 */ public void init(String orderId) { this.orderId = orderId; this.currentStatus = OrderStatusEnum.CREATED; // 初始状态 log.info("【订单上下文】订单初始化 | 订单ID:{},初始状态:{}", orderId, currentStatus.getDesc()); } /** * 触发事件(核心:委托当前状态处理事件,切换状态) */ public void triggerEvent(OrderEventEnum event) { log.info("\n【订单上下文】触发事件 | 订单ID:{},当前状态:{},事件:{}", orderId, currentStatus.getDesc(), event.getDesc()); // 获取当前状态实例 OrderState currentState = stateMap.get(currentStatus); if (currentState == null) { log.error("【订单上下文】未找到状态实例 | 状态:{}", currentStatus.getDesc()); return; } // 委托当前状态处理事件,获取新状态 OrderStatusEnum newStatus = currentState.handleEvent(this, event); if (!newStatus.equals(currentStatus)) { this.currentStatus = newStatus; log.info("【订单上下文】状态切换成功 | 订单ID:{},新状态:{}", orderId, newStatus.getDesc()); } } }
6. 订单服务(业务入口)
import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * 订单服务(业务入口,封装状态机调用) */ @Slf4j @Service public class OrderService { @Resource private OrderContext orderContext; /** * 创建订单 */ public void createOrder(String orderId) { orderContext.init(orderId); } /** * 支付订单 */ public void payOrder(String orderId) { orderContext.setOrderId(orderId); // 绑定订单ID orderContext.triggerEvent(OrderEventEnum.PAY); } /** * 取消订单 */ public void cancelOrder(String orderId) { orderContext.setOrderId(orderId); orderContext.triggerEvent(OrderEventEnum.CANCEL); } /** * 发货订单 */ public void shipOrder(String orderId) { orderContext.setOrderId(orderId); orderContext.triggerEvent(OrderEventEnum.SHIP); } /** * 确认收货 */ public void receiveOrder(String orderId) { orderContext.setOrderId(orderId); orderContext.triggerEvent(OrderEventEnum.RECEIVE); } }
7. 客户端(Spring Boot 测试)
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; /** * 客户端:测试Spring版订单状态机 */ @SpringBootApplication public class SpringStateDemoApplication { public static void main(String[] args) { // 1. 启动Spring容器(自动加载所有状态类和上下文) ConfigurableApplicationContext context = SpringApplication.run(SpringStateDemoApplication.class, args); OrderService orderService = context.getBean(OrderService.class); // 2. 测试订单状态流转 System.out.println("======= 测试订单状态流转 ======="); String orderId = "O20240314003"; // 创建订单(初始状态:已创建) orderService.createOrder(orderId); // 支付订单(已创建→已支付) orderService.payOrder(orderId); // 发货订单(已支付→已发货) orderService.shipOrder(orderId); // 确认收货(已发货→已收货) orderService.receiveOrder(orderId); // 尝试取消已收货订单(不支持) orderService.cancelOrder(orderId); context.close(); } }
输出结果
【订单上下文】初始化状态映射,共加载4个状态类 ======= 测试订单状态流转 ======= 【订单上下文】订单初始化 | 订单ID:O20240314003,初始状态:已创建 【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已创建,事件:支付 【已创建状态】处理支付事件 | 订单ID:O20240314003 【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已支付 【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已支付,事件:发货 【已支付状态】处理发货事件 | 订单ID:O20240314003 【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已发货 【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已发货,事件:确认收货 【已发货状态】处理确认收货事件 | 订单ID:O20240314003 【订单上下文】状态切换成功 | 订单ID:O20240314003,新状态:已收货 【订单上下文】触发事件 | 订单ID:O20240314003,当前状态:已收货,事件:取消 【已收货状态】不支持事件 | 订单ID:O20240314003,事件:取消

四、状态模式的核心特点与适用场景

优点
  1. 消除分支判断:替代大量 if/else/switch,代码更简洁、易维护;
  2. 状态行为封装:每种状态的行为封装到独立类中,符合单一职责原则;
  3. 状态扩展灵活:新增状态只需实现抽象状态接口,无需修改原有代码(开闭原则);
  4. 状态切换可控:状态转换规则集中在状态类中,便于管理和修改;
  5. 行为与状态解耦:环境类无需知道状态的具体实现,只需委托状态类处理。
缺点
  1. 类数量膨胀:每种状态对应一个类,状态较多时会产生大量状态类(如 10 种状态 = 10 个状态类);
  2. 状态逻辑分散:状态转换规则分散在各状态类中,复杂状态流转时难以全局把控;
  3. 依赖状态类:环境类依赖所有状态类,增加了类之间的关联;
  4. 简单场景冗余:简单的状态判断(如 2-3 种状态)使用状态模式会增加复杂度。
适用场景
  1. 对象行为随状态变化:如订单状态流转、电梯运行状态、游戏角色状态;
  2. 存在大量状态判断分支:类中包含大量 if/else/switch 判断不同状态的行为;
  3. 状态流转规则明确:状态之间的转换规则清晰,且可能需要扩展;
  4. 工作流 / 审批流程:如请假审批(待审批 / 审批中 / 已通过 / 已驳回)、合同审批;
  5. 有限状态机(FSM):如通信协议状态、设备运行状态、状态机框架开发。

五、JDK/ Spring 中的原生应用

状态模式在框架中主要用于状态驱动的行为控制,以下是核心场景:

1.Spring StateMachine(Spring 状态机)
  • 抽象状态:State
  • 具体状态:自定义State实现;
  • 环境类:StateMachine
  • 核心逻辑:基于状态模式实现可配置的状态机,支持状态流转、事件触发、监听器扩展。
2. Java 线程状态(Thread.State)
  • 抽象状态:Thread.State(枚举);
  • 具体状态:NEWRUNNABLEBLOCKEDWAITINGTERMINATED
  • 环境类:Thread
  • 核心逻辑:线程的行为(start ()、wait ()、notify ())由当前状态决定,如 NEW 状态的线程只能 start (),TERMINATED 状态的线程无法再次 start ()。
3. JDBC 连接状态(Connection)
  • 抽象状态:连接状态(未连接 / 已连接 / 已关闭);
  • 具体状态:ClosedStateConnectedStateIdleState
  • 环境类:Connection
  • 核心逻辑:连接的行为(execute ()、close ()、commit ())由当前状态决定,如已关闭的连接无法执行 SQL。
4. Servlet 生命周期(Servlet)
  • 抽象状态:Servlet生命周期状态(初始化 / 运行 / 销毁);
  • 具体状态:InitStateServiceStateDestroyState
  • 环境类:HttpServlet
  • 核心逻辑:Servlet 的行为(init ()、service ()、destroy ())由当前生命周期状态决定。
5. Netty Channel 状态(ChannelState)
  • 抽象状态:ChannelState
  • 具体状态:CHANNEL_OPENCHANNEL_ACTIVECHANNEL_INACTIVECHANNEL_CLOSED
  • 环境类:Channel
  • 核心逻辑:Channel 的 I/O 行为(read ()、write ())由当前状态决定,如 CLOSED 状态的 Channel 无法读写。

六、状态模式 vs 策略模式(易混淆点)

状态模式和策略模式的类结构几乎一致,但设计意图完全不同:

维度状态模式策略模式
核心目的处理状态驱动的行为变化,状态由对象内部决定处理算法 / 策略的替换,策略由外部传入
状态 / 策略切换内部自动切换(如订单支付后自动切换为已支付状态)外部手动切换(如选择不同的排序算法)
状态 / 策略关系状态之间有明确的流转规则(如已创建→已支付)策略之间相互独立,无依赖关系
关注点状态与行为的绑定算法的灵活替换
典型场景订单状态、电梯状态、生命周期管理排序算法、支付方式、校验规则

总结

  1. 状态模式的核心是将对象的每种状态封装为独立类,对象行为由当前状态决定,通过状态类委托处理而非分支判断
  2. 核心角色包括环境类(维护当前状态)、抽象状态(定义行为接口)、具体状态(封装状态行为和转换规则);
  3. 状态模式适用于对象行为随状态变化、存在大量状态判断分支的场景,如订单状态流转、工作流审批;
  4. 状态模式与策略模式结构相似但意图不同:状态模式关注内部状态驱动的行为变化,策略模式关注外部策略的灵活替换。

http://www.jsqmd.com/news/477172/

相关文章:

  • 瑞芯微RK3568控制板PCB设计实战:从PMU布局到叠层优化的效率提升
  • AI应用落地新范式:从FDE到AgentOps的工程化演进
  • Hugging Face Transformers 介绍
  • vim 提升
  • MATLAB图像去阴影实战:如何用高斯模糊拯救你的背光照片(附完整代码)
  • Spring开发系列教程(2)——IoC容器
  • Arduino+ESP8266获取网络时间全攻略(附阿里云NTP服务器配置)
  • ESP32-CAM+4G DTU:构建远程图像采集与云存储系统
  • 2024年高外观CNC加工厂家权威推荐榜:谁才是真正的颜值担当? - 余文22
  • 从零到上线:如何用Firebase ML Kit为你的App添加人脸识别功能(2023最新版)
  • 从零构建企业级安全防御体系:P2DR2模型实战解析
  • 机器视觉面试必问:从空洞卷积到BatchNorm的20个高频考点解析
  • 批量无人值守装机(使用cobbler批量安装windows)
  • Beyond Early, Deep, and Late: A New Taxonomy for Multi-modal Fusion in Autonomous Driving
  • 从游戏加速到跨国办公:三大运营商骨干网对个人用户的实际影响与优化技巧
  • C语言-文件操作-6
  • Win11下CH340串口识别失败:从设备描述符错误到退耦电容的深度解析
  • 如何用阿里云镜像加速Rancher V2.9.0的Docker部署?完整配置教程
  • 神州数码AC设备二层与三层上线实战:子网划分与DHCP配置详解
  • 树莓派4B WiFi连接成功但无法上网?5分钟搞定DNS配置与静态IP设置
  • 重构实战:破解继承中的‘被拒绝的遗赠‘难题
  • Neo4j Desktop启动失败:断网竟成终极解法?
  • 微服务-02(请求路由、身份认证、配置管理)
  • Redis安全加固:如何正确设置临时与永久密码(附实战演示)
  • 用AI插件加速Java学习:IntelliJ IDEA+AI编程插件实战指南(附黑马程序员同款配置)
  • 【AI加持】基于PyQt5+YOLOv8+DeepSeek的结核杆菌检测系统(详细介绍)
  • 告别公网IP烦恼:手把手教你用Nginx+Cloudflare Tunnel安全访问内网站点
  • Label-Studio快速部署与实战指南
  • 家用路由器选购避坑指南:从百兆到千兆,这些细节决定网速上限
  • PyQt5相关论文方向扩充及技术特性解析