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

《代码整洁之道》第7章 错误处理 - 笔记

得墨忒耳定律

不要链式调用, 如 a.getB().getC().doSomething()

直接获取对象调用方法

数据传输对象(DTOs)

DTO (Data Transfer Object): 数据传输对象。这是一种典型的数据结构。 里面没有任何业务逻辑代码。它的唯一作用就是在不同的软件层次之间(比如从数据库层到服务层,或者从服务层到外部接口)传输数据。

第7章 错误处理

核心是用异常来清晰、强制、有上下文地处理“异常”情况,将错误处理代码与正常业务逻辑代码分离。同时,应尽量避免使用 null返回错误码,让代码更加健壮和易于理解。

使用异常替代返回错误码

  • 思想: 错误是“异常”情况,它阻碍了程序的正常流程。错误处理代码(如何应对问题)应该与正常业务逻辑代码(顺利时该怎么做)分开。
  • 为什么: 异常机制 (try-catch-finally) 将正常流程代码(try 块)和错误处理代码(catch 块)清晰地分开了。相比返回错误码(比如返回 -1 或 null),异常更难被忽略,能强制调用者处理潜在错误,并且携带更丰富的错误信息。

给出足够异常的上下文

抛出异常时,不要只抛一个泛泛的异常类型 , 应该提供清晰的、有业务意义的错误消息,并且如果捕获了下层抛出的异常,应该将其包含在你向上层抛出的新异常中 。

举例说明

// 1. DAO 层 (底层)
class UserRepository {// 这个方法负责将用户对象保存到数据库,可能因为数据库连接问题、SQL语法错误等抛出 SQLExceptionpublic void save(User user) throws SQLException {// ... JDBC 代码或 ORM 代码 ...// 假设这里发生了数据库错误,抛出了一个 SQLExceptionthrow new SQLException("Duplicate entry for primary key 'users.PK_users'");}
}// 2. Service 层 (中层)
// 定义一个 Service 层知道的业务异常类型
class UserSaveException extends Exception {public UserSaveException(String message, Throwable cause) {super(message, cause); // 调用父类 Exception 的构造函数,保存消息和原因}
}class UserService {private UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}// 这个方法负责保存用户业务逻辑,它调用 DAO 层public void registerUser(User user) throws UserSaveException { // Service 层向上抛出自己定义的业务异常// ... Service 层的其他业务逻辑 ...try {// 尝试调用 DAO 层的功能userRepository.save(user);} catch (SQLException e) {// !!! 在这里捕获底层的 SQLException !!!// !!! 在这里包装成新的业务异常,并包含上下文 !!!// 提供业务层面的错误描述,包含用户 ID 或其他关键信息String businessMessage = "注册用户失败,用户名为: " + user.getUsername();// 创建一个新的 UserSaveException 异常// 第一个参数是业务错误消息,第二个参数是捕获到的原始底层异常 (SQLException)throw new UserSaveException(businessMessage, e);}// ... Service 层的其他业务逻辑 ...}
}// 3. Controller 层 (顶层)
class UserController {private UserService userService;public UserController(UserService userService) {this.userService = userService;}// 这个方法处理用户注册请求public Response handleUserRegistrationRequest(Request request) {User user = // ... 从请求中解析用户数据 ...try {// 调用 Service 层的功能userService.registerUser(user);// 如果上面没抛异常,说明成功了return Response.ok("用户注册成功!");} catch (UserSaveException e) {// !!! 在这里捕获 Service 层抛出的业务异常 !!!// !!! 在这里进行最终的错误处理或日志记录 !!!// 打印业务错误消息 (来自 UserSaveException 的消息)System.err.println("处理注册请求失败: " + e.getMessage());// !!! 更重要的是,可以通过 getCause() 获取导致这个业务错误的技术原因 !!!Throwable rootCause = e.getCause();if (rootCause != null) {System.err.println("底层原因: " + rootCause.getMessage());// 可以进一步打印堆栈信息帮助调试// rootCause.printStackTrace();}// 返回一个用户友好的错误响应return Response.badRequest("注册失败,请稍后再试或联系管理员。");} catch (Exception e) {// 捕获其他非预期的错误System.err.println("处理注册请求时发生未知错误: " + e.getMessage());return Response.internalServerError("未知错误。");}}
}

这个例子说明了:

  • 底层的错误 (SQLException) 发生在它最相关的层次 (DAO)。
  • Service 层捕获了这个底层的技术错误,但它知道当前的业务上下文是“正在注册用户”。
  • Service 层将技术错误包装成了具有业务意义的 UserSaveException,并添加了业务相关的错误消息(比如包含了用户名)。
  • Controller 层捕获了 UserSaveException,它不需要知道底层是 SQLException 还是其他什么错误,它只知道“用户保存失败了”。它可以根据这个业务异常类型给用户一个通用的提示。
  • 当程序员或运维人员查看错误日志时,他们会看到 UserSaveException 的业务消息,快速了解“出错了什么业务”。如果需要深入排查,他们可以查看这个异常的 cause(原因),找到原始的 SQLException,从而了解具体是哪个数据库问题导致了业务失败。

一次只记录一处日志

  • 什么意思: 当同一个异常沿着调用链向上层抛出,并在多个 catch 块中被捕获时,只在异常被“最终处理”或程序决定无法恢复并终止流程的那一层记录日志。不要在每一个 catch 块里都记录一次日志。
  • 为什么: 如果每个捕获层都记录日志,当一个异常发生时,日志里会出现大量重复的、表示同一个错误的日志条目,信息非常嘈杂,很难找到原始的错误发生点和关键信息。

封装第三方 API

  • 什么意思: 当你的代码调用了第三方库或框架(比如一个数据库连接库、一个 HTTP 客户端库)的方法,而这些方法会抛出第三方库特有的异常时,不要让这些第三方异常直接传播到你的应用代码的各个角落。你应该在调用第三方库的地方捕获它们的异常,然后包装成你自己定义的、带有业务含义的异常再向上抛。
  • 为什么:
    • 降低耦合: 如果将来你更换了第三方库(比如从一个 HTTP 客户端库换到另一个),你只需要修改封装它们的那个地方的代码和异常包装逻辑,调用你的应用的其它部分代码不需要改动,因为它们只依赖于你定义的异常类型。
    • 提升语义: 你定义的异常(比如 UserCreationFailedException)比第三方的技术异常(比如 HttpClientTimeoutException)更能清晰地表达业务上的错误原因。

举例说明

假如封装腾讯云 COS

  • 引入 COS 的 SDK
  • 定义一个自己的 COS 业务异常类,继承 Exception
// --- 我们的应用代码 ---package com.myproject.storage.domain.exception; // 我们的应用自己的异常包// 定义我们自己的文件存储相关的业务异常基类
public class FileStorageException extends Exception {public FileStorageException(String message) { super(message); }public FileStorageException(String message, Throwable cause) { super(message, cause); }
}// 可以定义更具体的子类,但这里为了简洁,只用一个基类演示
// public class FileUploadFailedException extends FileStorageException { ... }
// public class FileNotFoundError extends FileStorageException { ... }
  • 封装自己的 COS 类,对自己项目更通用友好,抛出异常也不再是 COS 的异常,而是自定义的 COS 异常。
    package com.myproject.storage.infra; // 基础设施层,处理外部依赖import com.myproject.storage.domain.exception.FileStorageException; // 导入我们自己的异常
    // 导入腾讯云 COS SDK 的相关类和异常
    import com.qcloud.cos.auth.BasicCOSCredentials;
    import com.qcloud.cos.client.COSClient;
    import com.qcloud.cos.client.COSClient.PutObjectRequest; // 导入内部类需要完整路径
    import com.qcloud.cos.client.COSClient.CosClientException; // 导入内部类需要完整路径
    import com.qcloud.cos.client.COSClient.CosServiceException; // 导入内部类需要完整路径import java.io.File; // 需要用到 File 类// 这个类封装了腾讯云 COS 的文件存储功能
    public class CosStorageService {private final COSClient cosClient;private final String bucketName;// 构造函数,初始化 COSClient,隐藏 SDK 的初始化细节public CosStorageService(String secretId, String secretKey, String bucketName) {// 这里直接使用 BasicCOSCredentials,如果需要更复杂的认证,可以在这里处理BasicCOSCredentials cred = new BasicCOSCredentials(secretId, secretKey);// 这里的初始化细节,只有 CosStorageService 需要知道this.cosClient = new COSClient(cred);this.bucketName = bucketName; // 封装桶名称}/*** 上传文件到 COS。* 只向上层抛出我们自己的 FileStorageException。** @param key 文件在桶内的路径 (例如 "uploads/my-document.pdf")* @param localFile 需要上传的本地文件* @throws FileStorageException 如果上传过程中发生任何错误*/public void uploadFile(String key, File localFile) throws FileStorageException { // 只声明抛出我们自己的异常// 可以在这里进行一些我们自己的业务校验,不属于 COS SDK 的if (localFile == null || !localFile.exists()) {throw new FileStorageException("要上传的本地文件不存在或无效: " + (localFile != null ? localFile.getPath() : "null"));}try {// 1. (如果需要)将我们自己的文件对象转换为第三方 SDK 需要的格式PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, localFile);// 2. 调用腾讯云 COS SDK 的上传 APIcosClient.putObject(putObjectRequest);// 3. 如果 SDK 方法成功返回,说明上传成功} catch (CosServiceException e) {// !!! 捕获 COS 服务端异常 !!!// !!! 包装成我们自己的文件存储异常,包含业务上下文和原始异常 !!!String errorMsg = String.format("上传文件失败,COS 服务端错误。键: %s, 桶: %s. 错误码: %s, 状态码: %d",key, bucketName, e.getErrorCode(), e.getStatusCode());throw new FileStorageException(errorMsg, e); // 包装并传递原始异常 e 作为 cause} catch (CosClientException e) {// !!! 捕获 COS 客户端异常 (网络、参数等) !!!// !!! 包装成我们自己的文件存储异常,包含业务上下文和原始异常 !!!String errorMsg = String.format("上传文件失败,COS 客户端错误。键: %s, 桶: %s. 消息: %s",key, bucketName, e.getMessage());throw new FileStorageException(errorMsg, e); // 包装并传递原始异常 e 作为 cause} catch (Exception e) {// !!! 捕获其他任何非 COS SDK 的意外运行时异常 (虽然可能性低,但安全起见) !!!// !!! 包装成我们自己的文件存储异常 !!!String errorMsg = String.format("上传文件时发生意外错误。键: %s, 桶: %s.", key, bucketName);throw new FileStorageException(errorMsg, e); // 包装并传递原始异常 e 作为 cause}}// 提供关闭客户端的方法,隐藏 SDK 的关闭细节public void shutdown() {cosClient.shutdown();}
    }

    使用特殊情况对象

    这个原则主要用在那些**“找不到”或者“为空”是一种常见且预期内的结果,而不是真正的错误或异常**的场景。在这种情况下,相比返回 null 或抛出异常,返回一个实现了相同接口的“特殊情况对象”可能让代码更清晰。

    场景: 根据用户 ID 查找用户。在某些系统中,查找某个 ID 的用户可能找不到,这是一个正常的操作结果(比如用户 ID 不存在),而不是一个程序错误。 这样就能省去if (user != null) 判断

// 定义用户接口
interface User {String getId();String getName();boolean isLoggedIn();// ... 其他用户行为方法 ...// 添加一个方法来判断是否为特殊情况对象boolean isNull(); // 或 isSpecialCase();
}// 普通用户类实现 User 接口 (与上面反例中的 User 类类似,但实现了接口)
class RealUser implements User {private String id;private String name;private boolean loggedIn;public RealUser(String id, String name, boolean loggedIn) {this.id = id;this.name = name;this.loggedIn = loggedIn;}@Override public String getId() { return id; }@Override public String getName() { return name; }@Override public boolean isLoggedIn() { return loggedIn; }@Override public boolean isNull() { return false; } // 这是一个真正的用户
}// !!! 特殊情况对象:表示“没有找到”的用户 !!!
class NullUser implements User {// 可以是单例,因为所有“没有找到”的情况都用这同一个对象表示private static final NullUser INSTANCE = new NullUser();private NullUser() {}public static NullUser getInstance() { return INSTANCE; }// !!! 实现 User 接口的方法,提供“默认”或“空”的行为 !!!@Override public String getId() { return "null"; } // 或 "",表示没有实际 ID@Override public String getName() { return "Guest"; } // 或 "未知用户",提供一个默认名称@Override public boolean isLoggedIn() { return false; } // 没有找到的用户当然没有登录@Override public boolean isNull() { return true; } // 明确表示这是一个特殊情况对象
}// 查找用户的服务,找不到返回特殊情况对象
class UserService {private Map<String, User> users = new HashMap<>();public UserService() {// 添加一些模拟用户 (使用 RealUser)users.put("user1", new RealUser("user1", "Alice", true));users.put("user2", new RealUser("user2", "Bob", false));}/*** 根据 ID 查找用户。如果找不到,返回 NullUser 特殊情况对象。* (这是推荐的方式,用于非异常的“找不到”情况)*/public User findUserById(String userId) {// Map.get() 找不到时返回 nullUser user = users.get(userId);// 如果找到了返回真正的用户,如果没找到返回 NullUser 单例对象return user != null ? user : NullUser.getInstance();}
}// 调用 findUserById 的代码 (不再需要繁琐的 null 检查)
class UserProcessor {private UserService userService;public UserProcessor(UserService userService) {this.userService = userService;}public void processUserInfo(String userId) {User user = userService.findUserById(userId); // 返回的永远是一个 User 对象 (RealUser 或 NullUser)// !!! 不需要 null 检查了 !!! 可以直接调用 User 接口的方法System.out.println("找到用户: " + user.getName()); // 如果是 NullUser,会打印 "Guest"// 如果需要区分“真的用户”和“特殊情况对象”,可以使用 isNull() 方法if (!user.isNull()) { // 判断是否不是特殊情况对象 (即是 RealUser)if (user.isLoggedIn()) { // 可以直接调用方法System.out.println("用户 " + user.getName() + " 已登录。");} else {System.out.println("用户 " + user.getName() + " 未登录。");}// ... 其他使用 user 对象的方法调用 ...} else {// 处理没找到用户(特殊情况对象)的逻辑(如果需要一些特殊处理)System.out.println("这是一个特殊情况用户,不需要处理登录状态等细节。");}}
}// 反例类
// 调用 findUserById 的代码 (必须进行 null 检查)
class UserProcessor {private UserService userService;public UserProcessor(UserService userService) {this.userService = userService;}// 反例方法public void processUserInfo(String userId) {User user = userService.findUserById(userId);//反例// !!! 每次使用 user 对象之前,都必须进行 null 检查 !!!if (user != null) {// 处理找到用户的情况System.out.println("找到用户: " + user.getName());if (user.isLoggedIn()) {System.out.println("用户 " + user.getName() + " 已登录。");} else {System.out.println("用户 " + user.getName() + " 未登录。");}// ... 其他使用 user 对象的方法调用 ...} else {// 处理没找到用户的情况System.out.println("没有找到用户 ID 为 " + userId + " 的用户。");}}
}

不要返回 Null / 不要传递 Null

这是两个非常坚决的原则

  • 不要从函数返回 null: 返回 null 强制调用者在每次使用函数返回值时都要进行 null 检查,这既啰嗦又容易遗漏,是导致 NullPointerException 的主要原因。替代方案: 抛出异常、返回特殊情况对象、返回空集合(如 Collections.emptyList())。
  • 不要传递 null 作为参数: 函数接收 null 参数,意味着函数内部必须增加额外的逻辑来检查参数是否为 null,这增加了函数的复杂性,违反了“只做一件事”和“函数参数越少越好”的原则。它也通常表明调用代码的逻辑有问题。

相关文章:

《代码整洁之道》第7章 错误处理 - 笔记

得墨忒耳定律 不要链式调用&#xff0c; 如 a.getB().getC().doSomething()。 直接获取对象调用方法 数据传输对象&#xff08;DTOs&#xff09; DTO (Data Transfer Object)&#xff1a; 数据传输对象。这是一种典型的数据结构。 里面没有任何业务逻辑代码。它的唯一作用就…...

java-mybatis01

对象/关系映射ORM ORM完成面向对象的编程语言DAO关系数据库的映射后&#xff0c;开发人员可以利用面向对象设计语言的建议易用性&#xff0c;也可利用关系数据库的技术优势。 ORM把关系数据库包装成面向对象的模型&#xff0c;采用ORM框架后&#xff0c;应用程序不再直接访问底…...

单片机之间的双向通信

具体功能实现 甲单片机通过按键可以控制乙单片机的LED灯&#xff0c;而乙单片机通过可以让连接甲单片机的数码管数字自增加一。 定时器资源 3个定时器 中断系统 执行现程序的过程中&#xff0c;出现某些急需处理的异常情况或特殊请求&#xff0c;cpu暂时中止现行程序&…...

量子纠缠式架构:当微服务同时存在于所有节点时,CAP定理是否依然成立?

一、CAP定理的经典困境 1. 传统分布式系统的三维束缚 经典权衡案例&#xff1a; 系统类型选择代价银行核心系统CP故障时拒绝服务社交网络AP短暂数据不一致物联网平台CA网络中断即崩溃 二、量子纠缠的降维打击 1. 量子微服务的超距同步 # 量子纠缠服务示例&#xff08;Qiski…...

使用rsync和inotidy-tools来进行实时备份文件夹数据

在工作中遇到这个这么个需求&#xff0c;就是我们有一个samba的文件共享服务器&#xff0c;里面存了很多文件&#xff0c;然后我想实时备份这个samba共享文件的。 1、概述 只需要在源服务器上操作&#xff0c;并在源服务器上安装好rsync和inotidy-tools&#xff0c;目标服务器…...

Spring AI 实现智能对话

1. 实现效果 2. Spring Boot 3 后端 2.1 pom.xml <!-- 管理包依赖。通过 Spring AI 的 BOM 文件统一管理所有 Spring AI 相关依赖的版本&#xff0c;确保版本一致性&#xff0c;减少冲突 --><dependencyManagement><dependencies><!-- Spring AI -->…...

2025 网络安全技术深水区探索:从 “攻防对抗” 到 “数字韧性” 的范式跃迁

引言&#xff1a;当攻击成本趋近于零&#xff0c;防御逻辑必须重构 2025 年&#xff0c;网络安全领域正经历三重根本性变革&#xff1a; 攻击者门槛坍缩&#xff1a;生成式 AI 将网络钓鱼开发效率提升 300%&#xff0c;勒索软件即服务&#xff08;RaaS&#xff09;订阅用户突破…...

学习笔记—双指针算法—移动零

双指针算法 移动零 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进…...

计算机网络全栈精讲:从 TCP/UDP 原理到 Socket 编程与 HTTP 协议实战(含代码实现)

计算机网络作为现代信息技术的基石&#xff0c;支撑着互联网、物联网、云计算等众多领域的发展。无论是前端开发者、后端工程师&#xff0c;还是运维人员&#xff0c;深入理解计算机网络原理都至关重要。本文将从网络分层模型出发&#xff0c;逐步深入讲解 TCP/UDP 协议、Socke…...

IP地址如何切换到国内别的省份?一步步指导

使用换IP工具的主要目的是通过更换设备的公网IP地址来满足特定需求&#xff0c;例如绕过限制、保护隐私或完成特定任务。以下是常见的应用场景和原因&#xff1a; 一、ip应用场景 1. 绕过IP限制 访问地域限制内容&#xff1a;某些网站或服务&#xff08;如游戏、社交平台 &am…...

c++流对象

核心概念回顾&#xff1a; C 的流库 (<iostream>, <fstream>, <sstream>) 提供了一种统一的方式来处理输入和输出&#xff0c;无论数据是来自键盘、文件还是内存中的字符串。它们都基于 std::istream (输入流基类) 和 std::ostream (输出流基类)&#xff0c…...

华为IP(5)

交换机的堆叠与集群 堆叠和集群指的是同一件事 前言: 随着企业的发展&#xff0c;企业网络的规模越来越大&#xff0c;这对企业网络提出了更高的要求&#xff1a;更高的可靠性、更低的故障恢复时间、设备更加易于管理等。 传统的园区网高可靠性技术出现故障时很难做到毫秒级…...

零信任架构下的等保 2.0 与密评密改双合规

随着《网络安全等级保护2.0》和《商用密码应用安全性评估》的深入实施&#xff0c;企业面临双重合规压力&#xff1a;既要满足等保2.0对“主动防御”和“动态防护”的要求&#xff0c;又要通过密评密改强化密码技术的合规性。传统安全架构依赖边界防护和静态密码策略&#xff0…...

华为 MRAG:多模态检索增强生成技术论文阅读

GitHub项目链接&#xff1a;https://github.com/PanguIR/MRAGSurvey 总览 多模态检索增强生成&#xff08;MRAG&#xff09;通过将文本、图像、视频等多模态数据整合到检索与生成过程中&#xff0c;显著提升了多模态大语言模型&#xff08;MLLM&#xff09;的性能。传统检索增…...

文字光影扫过动效

列子1 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>光影扫过文字动效</title><styl…...

SpringBoot配置RestTemplate并理解单例模式详解

在日常开发中&#xff0c;RestTemplate 是一个非常常用的工具&#xff0c;用来发起HTTP请求。今天我们通过一个小例子&#xff0c;不仅学习如何在SpringBoot中配置RestTemplate&#xff0c;还会深入理解单例模式在Spring中的实际应用。 1. 示例代码 我们首先来看一个基础的配置…...

计算机网络应用层(5)-- P2P文件分发视频流和内容分发网

&#x1f493;个人主页&#xff1a;mooridy &#x1f493;专栏地址&#xff1a;《计算机网络&#xff1a;自顶向下方法》 大纲式阅读笔记_mooridy的博客-CSDN博客 &#x1f493;本博客内容为《计算机网络&#xff1a;自顶向下方法》第二章应用层第五、六节知识梳理 关注我&…...

C++ TCP通信原理与实现

C 中 TCP 通信的原理基于 TCP/IP 协议栈的实现&#xff0c;以下是核心原理和关键步骤的详细说明&#xff1a; 一、TCP 通信核心原理 面向连接 通信双方需通过 三次握手 建立可靠连接&#xff0c;确保通信通道稳定。通过 四次挥手 断开连接&#xff0c;保证数据完整性。 可靠传…...

计算机网络-运输层(1)

计算机网络-运输层(1) 文章目录 计算机网络-运输层(1)5.1 运输层概述5.2 运输层端口号、复用与分用端口号基本概念端口号特性端口号分类重要说明 5.3 UDP与TCP协议对比关键区别说明 5.1 运输层概述 计算机网络体系结构中的物理层、数据链路层以及网络层共同解决了主机通过异构…...

学习spark-streaming收获

1.流处理的核心概念 •实时 vs微批处理&#xff1a;理解了 Spark Streaming 的微批处理&#xff08;Micro-Batch&#xff09;模型&#xff0c;将流数据切分为小批次&#xff08;如1秒间隔&#xff09;进行处理&#xff0c;与真正的流处理&#xff08;如Flink&#xff09;的区…...

蓝桥杯 14. 奇怪的数列

奇怪的数列 原题目链接 题目描述 从 X 星截获一份电码&#xff0c;是一些数字&#xff0c;如下&#xff1a; 13 1113 3113 132113 1113122113 ⋯⋯YY 博士经彻夜研究&#xff0c;发现了规律&#xff1a; 第一行的数字随便是什么&#xff0c;以后每一行都是对上一行 “读出…...

前端高频面试题day2

如何在vue3中使用defineAsyncComponent实现异步组件加载 在 Vue 3 中&#xff0c;使用 defineAsyncComponent 实现异步组件加载的步骤如下&#xff1a; 引入方法&#xff1a;从 Vue 中导入 defineAsyncComponent。定义异步组件&#xff1a;通过 defineAsyncComponent 包装一个…...

Linux系统之设置开机启动运行桌面环境

Linux 开机运行级别介绍与 Ubuntu 桌面环境配置指南 一、Linux 开机运行级别(Runlevel) 在传统的 Linux 系统(如 SysV init 初始化系统)中,运行级别定义了系统启动时加载的服务和资源。常见的运行级别如下: 运行级别模式用途0Halt(停机模式)关闭系统1Single User Mode…...

Python PyAutoGUI库【GUI 自动化库】深度解析与实战指南

一、核心工作原理 底层驱动机制&#xff1a; 通过操作系统原生API模拟输入使用ctypes库调用Windows API/Mac Cocoa/Xlib屏幕操作依赖Pillow库进行图像处理 事件模拟流程&#xff1a; #mermaid-svg-1CGDRNzFNEffhvSa {font-family:"trebuchet ms",verdana,arial,sans…...

【MobaXterm】win10下载v25.1安装流程

【下载地址】 官网&#xff1a; https://mobaxterm.mobatek.net/ 下载安装版&#xff0c;解压使用更快一些 【v20.0中文安装包】 夸克网盘&#xff1a;https://pan.quark.cn/s/2ad5b59e6d8e#/list/share 对应的指导教程&#xff1a; MobaXterm中文版安装使用教程-附安装包…...

2025.4.22 JavaScript 常用事件学习笔记

一、事件概述 JavaScript 事件是指在用户与网页交互或网页状态发生变化时所触发的操作。通过使用事件&#xff0c;可以为网页添加丰富的动态功能&#xff0c;实现用户与页面之间的互动&#xff0c;让网页不再只是静态的展示内容。 二、常见鼠标事件 click 事件 简介 &#xf…...

Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现

Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现 文章目录 需求&#xff1a;参考资料架构图了解Camera相关专栏零散知识了解部分相机源码参考&#xff0c;学习API使用&#xff0c;梳理流程&#xff0c;偏应用层Camera2 系统相关 修改文件-修改方案修改文件&#xff1a;修改…...

Linux:基础IO 文件系统

Linux:基础IO && 文件系统 一、系统IO&#xff08;一&#xff09;系统文件操作接口1、open2、write3、read &#xff08;二&#xff09;文件描述符1、概念2、标准输入、标准输出、标准错误 &#xff08;三&#xff09;dup系统调用&#xff08;重定向原理&#xff09; 二…...

近期有哪些断链危机?如何提升供应链风险管理能力?

全球供应链格局正经历深刻变革&#xff0c;其网络架构愈发复杂&#xff0c;涉及多国企业主体且涵盖多个节点与复杂环节&#xff0c;管理难度显著增大。从原材料采购到终端交付&#xff0c;运输、仓储、加工等任一环节均存在潜在风险&#xff0c;单一环节效率滞后易引发系统性连…...

知识科普|褪黑素的发展历程及应用研究进展

睡眠作为维持人体健康的重要生理机制&#xff0c;其节律性受到精密调控。在昼夜节律系统的调控下&#xff0c;人类普遍遵循周期性单次睡眠模式&#xff0c;这一过程涉及复杂的神经-体液调控网络。其中神经元活动、神经递质传导、激素分泌及遗传调控机制共同作用于睡眠觉醒系统&…...

企业如何构建一个全面的Web安全防护体系

企业如何构建一个全面的Web安全防护体系 企业构建全面的Web安全防护体系需融合战略规划、技术防御、持续运营和风险治理四大维度&#xff0c;以下是基于行业最佳实践的系统化方案&#xff1a; 一、顶层设计&#xff1a;治理架构与安全战略 战略规划与合规驱动 制定网络安全愿…...

T8332FN凯钰LED驱动芯片多拓扑车规级AEC-Q100

T8332FN是一款支持多拓扑结构的恒流LED驱动控制芯片&#xff0c;适用于汽车照明及高功率LED应用&#xff0c;具备宽电压输入、高精度调光及多重保护功能。 核心特性 - 输入与拓扑&#xff1a;支持5-60V宽电压输入&#xff0c;适配Boost、Buck、Buck-Boost、SEPIC四种拓扑结构&…...

Redis一些小记录

Redis一些小记录 SpringData Redis&#xff1a;RedisTemplate配置与数据操作 操作String类型数据 String是Redis中最基本的数据类型&#xff0c;可以存储字符串、整数或浮点数。RedisTemplate提供了ValueOperations接口来操作String类型的数据&#xff0c;支持设置值、获取值、…...

Win10安装 P104-100 驱动

安装完之后总结一下, 之前做了不少功课, 在网上搜了很多教程, 视频的文字的, 但是很多已经比较陈旧了. 最后发现的这个 GitHub 项目 NVIDIA-patcher 是最有用的, 因为这是现在这些魔改驱动的来源. NVIDIA-patcher 仓库地址: https://github.com/dartraiden/NVIDIA-patcher 安…...

Android开机动画资源包制作(测试使用)

开机动画资源包需要采用仅存储的方式进行压缩&#xff0c;不能使用压缩软件直接压缩生成。 如果是系统开发人员&#xff0c;可以在源码目录中&#xff0c;采用bootanim程序提供的制作方式进行&#xff0c;下面可供测试人员自行制作。 制作流程 1&#xff09;基于设备中已有开…...

PWN基础-利用格式化字符串漏洞泄露canary结合栈溢出getshell

测试源码&#xff1a; #include<stdio.h> void exploit() {system("/bin/sh"); } void func() {char str[0x20];read(0, str, 0x50);printf(str);read(0, str, 0x50); } int main() {func();return 0; } 编译&#xff0c;开启 canary 保护&#xff0c;关闭 p…...

Kafka HA集群配置搭建与SpringBoot使用示例总结

Kafka HA集群配置搭建与SpringBoot使用示例总结 一、Kafka高可用(HA)集群搭建 1. 环境准备 至少3台服务器&#xff08;推荐奇数台&#xff0c;如3、5、7&#xff09;已安装Java环境&#xff08;JDK 1.8&#xff09;下载Kafka二进制包&#xff08;如kafka_2.13-3.2.1.tgz&…...

MSO-Player:基于vlc的Unity直播流播放器,支持主流RTSP、RTMP、HTTP等常见格式

MSO-Player 基于libVLC的Unity视频播放解决方案 支持2D视频和360度全景视频播放的Unity插件 &#x1f4d1; 目录 &#x1f3a5; MSO-Player &#x1f4cb; 功能概述&#x1f680; 快速入门&#x1f4da; 关键组件&#x1f4dd; 使用案例&#x1f50c; 依赖项&#x1f4cb; 注意…...

97A6-ASEMI无人机专用功率器件97A6

编辑&#xff1a;ll 97A6-ASEMI无人机专用功率器件97A6 型号&#xff1a;97A6 品牌&#xff1a;ASEMI 封装&#xff1a;SOT-23 批号&#xff1a;最新 引脚数量&#xff1a;3 特性&#xff1a;双向可控硅 工作温度&#xff1a;-40℃~150℃ ‌97A6双向可控硅&#xff1a…...

body Param Query 三个 不同的入参 分别是什么意思 在前端 要怎么传 这三种不同的参数

在 NestJS 中&#xff0c;Body()、Param() 和 Query() 用于处理不同类型的请求参数。以下是它们的含义及前端传递方式&#xff1a; Body()&#xff1a;请求体参数 • 含义&#xff1a;用于获取请求体中的数据&#xff08;如 POST/PUT 请求中提交的 JSON、表单数据等&#xff09…...

生成式人工智能认证(GAI认证)含金量怎么样?

当生成式人工智能(Generative AI)的浪潮以摧枯拉朽之势重塑职业版图时,一个尖锐的问题正悬在无数人的心头:在技术迭代比眨眼更快的时代,如何证明自己具备驾驭AI的核心能力? 这场认知革命的背后,一张认证证书的价值早已超越了纸面——它既是个人能力的“信用背书”,也是…...

环境DNA宏条形码技术,鱼类检测引物如何选择?

环境DNA&#xff08;eDNA&#xff09;宏条形码技术在鱼类多样性调查研究中的优势明显&#xff0c;相比于传统调查方式&#xff0c;eDNA宏条形码技术灵敏度更高&#xff0c;能够更好地揭示鱼类的丰富度&#xff0c;并且具有高时效性。然而&#xff0c;在使用这个技术的过程中&am…...

Scala集合操作与WordCount案例实战总结

集合计算简单函数 1、说明 &#xff08;1&#xff09;求和 &#xff08;2&#xff09;求乘积 &#xff08;3&#xff09;最大值 &#xff08;4&#xff09;最小值 &#xff08;5&#xff09;排序 2、案例实操 object demo29{ def main(args: Array[String]): Unit { val…...

Spark-Streaming核心编程(四)总结

有状态转化操作 - UpdateStateByKey ‌功能描述‌ UpdateStateByKey原语用于在DStream中跨批次维护状态&#xff0c;例如流计算中的累加wordcount。 它允许对一个状态变量进行访问和更新&#xff0c;适用于键值对形式的DStream。 ‌工作原理‌ 给定一个由(键&#xff0c;事…...

关系型数据库PostgreSQL for Mac 保姆级使用教程

第一部分&#xff1a;安装PostgreSQL 方法一&#xff1a;使用Postgres.app&#xff08;最简单&#xff09; 访问 Postgres.app官网 下载最新版本&#xff0c;将 Postgres.app 移动到 “Applications” 文件夹。 双击Postgres.app打开应用&#xff0c;点击"Initialize&q…...

新增 29 个专业,科技成为关键赛道!

近日&#xff0c;教育部正式发布《普通高等学校本科专业目录&#xff08;2025年&#xff09;》&#xff0c;新增 29 个本科专业&#xff0c;包括区域国别学、碳中和科学与工程、海洋科学与技术、健康与医疗保障、智能分子工程、医疗器械与装备工程、时空信息工程、国际邮轮管理…...

云计算市场的重新分类研究

云计算市场传统分类方式&#xff0c;比如按服务类型分为IaaS、PaaS、SaaS&#xff0c;或者按部署模式分为公有云、私有云、混合云。主要提供计算资源、存储和网络等基础设施。 但随着AI大模型的出现&#xff0c;云计算市场可以分为计算云和智算云&#xff0c;智算云主要是AI模…...

大模型时代的具身智能:从虚拟到现实的智能体进化革命

一、具身智能&#xff1a;重新定义 AI 与物理世界的交互范式 &#xff08;一&#xff09;概念解析&#xff1a;从 "离身" 到 "具身" 的认知革命 具身智能&#xff08;Embodied AI&#xff09;是融合大模型决策能力与物理实体执行能力的新型智能系统&…...

鸿蒙NEXT开发正则工具类(ArkTs)

import { FormatUtil } from ./FormatUtil;/*** 正则工具类* author CSDN-鸿蒙布道师* since 2025/04/27*/ export class RegexUtil {/*** 英文字母、数字和下划线*/static readonly REG_GENERAL "^\\w$";/*** 数字*/static readonly REG_NUMBERS "^\\d$"…...

Flink维表深度解析

一、维表的概念与作用 维表&#xff08;Dimension Table&#xff09; 是数据仓库中的核心概念&#xff0c;通常用于存储静态或缓慢变化的业务实体信息&#xff08;如用户资料、商品信息、地理位置等&#xff09;。在实时流处理场景中&#xff0c;维表的作用是为主数据流&#…...