Java异常处理全解析:从继承体系到底层原理与最佳实践
1. 先看异常继承体系一张图理清所有家族成员做Java开发这么多年我见过太多人在异常处理上翻车。有的是catch了Exception却没处理日志打出来一堆堆栈却不知道根因在哪有的是在循环里抛出受检异常结果编译都过不了干脆把方法签名上加了个throws Exception糊弄过去还有的干脆在最外层catch(Exception e)后直接return null接口出错毫无感知。说到底这些问题的根源大多是对异常体系本身没有一个完整的认知。Java的异常处理机制并不是try包一下、catch接一下这么简单它背后是一套完整的设计哲学。先把继承结构吃透后面所有疑难杂症都能顺着根找到答案。Java的异常体系以Throwable为总基类往下分成两大阵营Error和Exception。Error这兄弟在JVM层面出了大问题才会登场比如OutOfMemoryError、StackOverflowError。它表示程序无法恢复的严重问题正常情况下你不应该去catch它因为捕获了也没意义——内存都不够了你catch住还能干啥这种错误应当让程序终止而不是试图救活它。Exception是真正需要业务侧关注的部分又可以细分为两类。一类叫受检异常checked exception比如IOException、SQLException编译器要求你必须在代码层面处理它要么try-catch要么在方法签名上throws抛出去。另一类是非受检异常unchecked exception也就是RuntimeException的子类比如NullPointerException、IllegalArgumentException、ClassCastException。编译器不强制你处理你不写try-catch也能编译通过但运行期一炸就是一场事故。public class ExceptionDemo { public static void main(String[] args) { // 受检异常编译器强制处理 // 不处理的话编译直接报错 try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.out.println(线程被中断); } // 非受检异常编译不强制运行期才暴露 String name null; // name.length(); // 运行期抛 NullPointerException if (name ! null) { System.out.println(name.length()); } } }我在实际工作中见过有人试图catchOutOfMemoryError想把堆内存释放掉再继续跑结果当然是JVM直接崩了。遇到Error别抱幻想该让进程退就让它退容器会自动拉起新实例比你硬撑着强得多。还有一点容易被忽略RuntimeException本身就是Exception的子类。也就是说你在catchException的时候其实把RuntimeException也一起兜住了。这既是好事也是坏事——好事是能兜底坏事是很可能在catch块里把所有运行时异常一并吞掉导致问题被掩盖。1.1 异常类的代码位置与继承关系实践看代码的时候可以通过IDE的继承关系图很直观地看到这一家族的全部成员。常见的NullPointerException继承自RuntimeExceptionRuntimeException继承自ExceptionException继承自ThrowableThrowable实现了Serializable接口。这里有个小细节Throwable实现了Serializable意味着异常对象可以被序列化。这在分布式系统里特别重要因为异常信息经常需要跨越网络传输。不过要注意异常对象里如果持有非序列化的字段比如某个网络连接对象序列化时会直接抛NotSerializableException。写自定义异常时字段尽量只用基础类型和字符串少装业务对象。1.2 检查异常与运行时异常的边界把握判断一个异常是受检还是非受检有个土办法看它是不是RuntimeException的直接或间接子类。IOException不是这就是受检异常。IllegalStateException是这就是非受检异常。关键来了什么时候该用受检异常什么时候该用非受检异常受检异常适合表达调用方必须知道的预期失败比如文件不存在、网络超时。非受检异常适合表达程序本身有bug比如参数校验失败、空指针、数组越界。我在实际项目里经常看到一种反模式所有方法都声明throws Exception把受检异常层层上抛结果就是你在最上层catch Exception时根本区分不了是什么问题。正确做法是受检异常抛出后在真正有能力处理它的地方去catch中间层能上抛就上抛但要做好方法签名的语义声明。别把所有异常都抽象成Exception否则异常处理等于白做。2. 异常处理的关键字与语法细节try、catch、finally、throw、throwsJava里异常处理的核心关键字就这几个try用来包裹可能出错的代码块catch用来捕获并处理某类异常finally用来执行无论是否异常都必须跑的收尾逻辑throw是手动抛出一个异常对象throws是声明方法可能抛出的异常类型。单独拆开看每个关键字都不难理解但组合起来就有无数细节了。我重点说几个日常最容易踩坑的地方。2.1 finally在return时到底怎么执行先看这段代码猜猜输出什么public class FinallyReturn { public static void main(String[] args) { System.out.println(calc()); } private static int calc() { int i 1; try { i 2; return i; } finally { i 3; } } }输出结果是2不是3。原因在于return i在执行时先把当前i的值2保存到返回值槽位然后才去执行finally块。finally里虽然把局部变量i改成了3但返回值槽位里存的还是2。这还不是最坑的。如果finally里也有return那就会覆盖try里的return结果private static int calc() { try { return 1; } finally { return 2; } }输出的就是2。本来finally块的价值是保证清理逻辑一定执行你却在里面塞return这属于严重反模式代码看起来像是try里成功返回了实际结果却被偷偷替换掉。我在代码审查里看到这种写法会直接打回因为调试时非常容易误导。2.2 try-with-resources才是资源关闭的一等公民传统写法里需要在finally里手动关闭IO流各种null判断和try-catch嵌套让代码又臭又长。从Java 7开始有了try-with-resources语法只要资源类实现了AutoCloseable或Closeable接口就可以用这种写法自动关闭String path data.txt; try (BufferedReader reader new BufferedReader(new FileReader(path))) { String line; while ((line reader.readLine()) ! null) { System.out.println(line); } } catch (IOException e) { System.out.println(读取文件失败 e.getMessage()); e.printStackTrace(); }当try代码块执行完毕退出时JVM会自动调用资源的close()方法。如果业务代码和close方法都抛出了异常close阶段的异常会被压制suppressed同时业务异常正常抛出压制异常可以通过Throwable.getSuppressed()拿到。这个机制的巧妙之处在于避免了异常覆盖。如果还用传统finally写法很容易出现业务异常被close异常覆盖的经典问题。除非在finally里再写try-catch去包裹close否则异常信息就丢了。这正是try-with-resources的另一个好处异常信息不会因为资源关闭失败而丢失。2.3 throw和throws各司其职throw和throws虽然长得像但角色完全不同。throw是语句后面跟一个异常对象实例作用是实际把异常抛出去public void checkAge(int age) { if (age 0 || age 150) { throw new IllegalArgumentException(年龄参数不合法age age); } }throws是方法签名声明告诉调用方我这个方法可能抛这些异常你来处理public String readFile(String path) throws IOException { // 不catch IOException直接上抛 }这里有个选择题什么时候自己catch什么时候throws上抛原则很简单如果当前方法能处理这个异常场景比如可以重试、可以降级、可以给用户友好提示就catch住如果当前方法没有能力处理或者需要让调用方根据业务场景决定那就throws上抛。我在项目里还经常看到一种情况方法根本不会抛异常的代码却也写上throws Exception。这种写法纯属干扰信息看代码的人会误以为调用的方法有失败点凭空增加心理负担。方法签名上的异常声明要精确能少写就少写但该写的一定要写。3. 异常机制底层原理与性能开销JVM到底做了什么知道了语法还不够深入了解异常处理的底层机制才能解释为什么有些代码慢也才能写出更优雅的异常处理代码。3.1 异常表与异常处理流程JVM在字节码层面是通过异常表来实现异常处理的。你用javap -c反编译一个带try-catch的方法可以看到异常表中记录了每条异常处理规则的起始行号、结束行号、跳转目标和异常类型。当代码执行到某一指令抛出异常时JVM会在当前方法的异常表里寻找匹配项。匹配规则是异常的抛出点要在try块的起始和结束位置之间异常类型要是catch声明类型的子类。找到后程序跳转到catch块继续执行。如果当前方法里没找到匹配项就把异常往上抛给调用方调用方再重复这个过程。这种逐层查找的机制就是异常传播链的本质。这里有一个容易忽视的点异常表的判断逻辑是有性能开销的。正常情况下JVM对进入和退出try块做了优化几乎不产生额外成本。专门做性能测试的几个基准里没有异常发生时try块执行速度和不加try的版本差别很小。真正昂贵的操作是异常对象的创建和堆栈轨迹的填充——Throwable.fillInStackTrace()会遍历整个调用栈做快照这个动作在万分紧急的报错瞬间非常费时。3.2 异常的性能开销到底多大说一个我实测过的场景单线程循环100万次每次抛出一个普通的RuntimeException再catch住。同样的逻辑不抛异常情况下几毫秒完成抛异常版本耗时接近几十倍甚至上百倍的差距。这不是危言耸听异常对象创建时的堆栈填充确实是个重活。所以在高并发、高频率的代码路径上比如每次请求的处理入口要小心使用异常来控制业务流程。典型的反模式就是用异常做流程控制// 反模式用异常判断密码是否正确 try { checkPassword(userInput); return 登录成功; } catch (PasswordWrongException e) { return 密码错误; }密码错了就抛出异常这等于每次错误尝试都在V8上全栈快照性能开销大还不好维护。正确做法是用返回值或状态码表达业务流程的分支只有真正的异常场景才抛异常。有一种例外情况有些底层库确实会用异常来表达预期内的失败比如synchronized的悲观锁获取失败。但这种库通常做了大量优化你不用盲目模仿。3.3 异常链与日志中的堆栈轨迹看异常堆栈时最重要的是头三行异常类型、信息、第一个at行的位置。那才是异常真正的发生点。多段日志里经常看到java.lang.NullPointerException at com.example.service.OrderService.calcPrice(OrderService.java:88)这种堆栈说明了空指针的爆发地但没说为什么为空。所以实际排查时还要再往前翻日志找业务上下文。我通常会在catch里加一条带业务标识的上下文日志配合异常堆栈才能把问题定位到具体的订单号、用户或请求参数。另外异常链设计也很关键。调用方catch住底层异常后如果想把它换成上层业务异常再抛出去要记得把原始异常传进构造器这样可以保留完整调用链catch (IOException e) { throw new BizException(文件下载失败fileId fileId, e); }如果直接throw new BizException(文件下载失败)原始异常的堆栈就丢了排查问题时会非常痛苦。4. 异常处理的设计范式与最佳实践在真实项目里异常处理做得好不好直接决定线上问题排查效率。我来按实际开发场景梳理几个关键实践。4.1 自定义异常精准表达业务失败很多项目一上来就继承RuntimeException写一个BizException然后所有业务校验失败都往里丢。这比直接用Exception好但还不够好。更好的实践是按模块设计异常类型。比如用户模块有UserNotExistException、UserForbiddenException支付模块有PayTimeoutException、InsufficientBalanceException。每个异常专门表达一类业务失败调用方catch的时候才能准确区分处理而不是catch住一个大杂烩异常后再根据message里的字符串去判断。自定义异常时至少实现三个构造器无参、带message、带message和cause。原因很简单后续调用场景多样有的需要直接指定异常信息有的需要携带底层失败原因。如果构造器不全使用的时候就只能绕过super()写法会变得非常别扭。也可以考虑加一些辅助字段比如错误码方便前端做国际化映射。但要控制字段数量别把异常当DTO用。4.2 捕获粒度与吞异常问题吞异常是我最常review到的坏味道。典型表现是catch里啥也没干或者只打一行System.out.println。以下这两种写法都属于无效异常处理// 写法一直接吞掉 try { // ... } catch (Exception e) { } // 写法二打印后继续 try { // ... } catch (Exception e) { e.printStackTrace(); }打印堆栈和生产环境的日志体系完全脱节特别是在业务系统里用System.out打日志线上根本捞不到。正确做法是用日志框架如Slf4j并打上业务上下文信息。捕获粒度也有讲究。如果try块里可能抛出多种异常尽量分别catch处理。一旦用一个大catch (Exception)兜底全部逻辑各种问题的处理方式就没了区分度。我见过一个项目里有个接口偶尔超时查了半天发现对比对端返回的不同错误码处理方式全被统一成了返回空数组——本该是失败重试的场景变成了假装成功。4.3 日志规范每条异常日志都带上上下文记录异常信息时我一般遵循一个经验传四个层面的信息。第一层面异常堆栈本身第二层面当前业务操作是什么比如用户下单第三层面影响的关键标识比如userId、orderId第四层面尝试了什么处理比如已重试两次交给死信队列。import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger log LoggerFactory.getLogger(OrderService.class); public void createOrder(OrderCreateRequest request) { try { // ... } catch (Exception e) { log.error([createOrder] 创建订单失败, userId{}, goodsId{}, request.getUserId(), request.getGoodsId(), e); } }日志关键字是log.error。log.warn表示预期中的轻微问题log.error表示流程确实断了。两者不要混用否则告警分级就没意义了。4.4 顶层统一异常处理在Web应用里每个接口方法都写try-catch完全不现实也没必要。更好的做法是做一个全局异常处理器例如在SpringBoot里用RestControllerAdvice统一拦截异常返回。这样设计的好处显而易见业务层只负责抛异常不用关心HTTP状态码和响应体结构统一拦截器负责把异常翻译成对外响应的错误码和提示信息。受检异常、运行时异常、业务异常各归各位代码可读性和一致性都大幅提升。我还会在全局异常处理器里做一层兜底catch (Exception e)打上error日志并返回500。这样即使有漏网之鱼也不会把整个堆栈直接吐给前端。接口响应不能泄露异常内部的类名和调用栈安全上这是必须的。5. 常见异常场景排查与踩坑速查这一节整理几个真实项目中频繁遇到的具体异常场景直接给出排查思路。5.1 NullPointerException的定位与规避空指针大概是Java里出现频率最高的异常。它本身并不神秘就是对象引用为null时调用了方法或访问了属性。排查时最重要的信息是堆栈里的at行号。看这段at com.example.service.UserService.getUserInfo(UserService.java:45)解法是去线号45看是哪一行代码那一行上肯定有一次成员方法调用或属性访问那个this.toString()的位置或者点号左边的对象变量就是null来源。规避空指针我习惯几个方法方法入参如果必填就前置校验null直接抛IllegalArgumentException。返回值如果是可能缺失的数据用Optional表达让调用方感知到可能为空。集合查询返回null的情况统一返回空集合。必要时借助工具类的空安全集合包装。5.2 ClassCastException类型转换之前先判断ClassCastException会在强转类型时发生。排查思路是搞清楚这个对象的真实类型是从哪来的。最常见的场景是通过Object接收反序列化结果或反射结果然后直接强转。稳妥做法是先instanceof判断Object obj getObjectFromSomeWhere(); if (obj instanceof UserDTO dto) { // 安全使用 dto } else { log.error(类型不匹配, expectedUserDTO, actual{}, obj null ? null : obj.getClass().getName()); }instanceof模式匹配是Java 16引入的用起来比传统先判断后强转简练不少。5.3 并发环境下的异常信息丢失并发环境还有一种隐蔽问题。比如你把异常信息放进一个HashMap或可变对象里多个线程同时写结果互相覆盖等主线程去查看时已经面目全非。这类问题排查起来特别费劲看起来没有异常抛出来但状态明显不对。解决方法是始终用线程封闭的方式传递异常信息。每个线程有独立的上下文不要共享一个异常对象。实在要合并结果用线程安全的数据结构并且给每条异常带上线程标识。5.4 受检异常与接口设计不协调的典型场景有一种场景我刚工作时经常犯在Java 8 Stream的lambda表达式里调用了一个声明throws受检异常的方法编译直接报错。这是因为Function接口的apply方法没有声明throws Exception你没法在lambda里直接抛出受检异常。我看到过的解决办法有三种第一种是在lambda内部try-catch处理把受检异常转换为非受检异常再抛第二种是用SneakyThrows这类技巧绕过编译检查不过看着就很魔法不太用第三种是避免在流式链路里调用这类方法先用普通循环把数据处理好。个人推荐第一种和第三种。尤其是批量处理一批文件时我更倾向用普通for循环配合try-catch因为方便定位具体哪个文件失败也方便做失败跳过或终止的策略。6. 一段话总结我这些年对异常处理的体会如果让我只给一个新上手Java的开发者说一句关于异常的忠告我会说异常是用来表达意外情况的不是用来控制正常流程的工具。该抛就抛该catch就catch但千万别把catch当成垃圾桶什么都往里丢踩一脚算完。我自己在实际开发里踩过的最深的坑就是当年在线上环境把异常堆栈打到了控制台结果出了故障翻日志时一无所获。后来老老实实接入了统一日志框架、建了全局异常处理器、梳理了自定义异常的继承关系排障效率立竿见影。另一个我现在很坚持的实践是任何时候catch住异常至少要留一条日志痕迹。哪怕只是打个warn说这个异常暂时忽略原因是XXX也比纯空catch好。因为代码是会演进的当时看起来无需处理的异常未来某个版本可能就是重大故障的源头有日志就有了追溯的可能性。Java异常处理机制说到底是工程问题不是语法问题。理解了体系掌握了细节再结合项目实际去设计好属于自己的异常处理规范你会发现在Java生态里异常处理反而是个比较可靠的保障机制。

相关新闻

FISCO BCOS 存证合约实战:从普通部署到可升级架构的完整指南

FISCO BCOS 存证合约实战:从普通部署到可升级架构的完整指南

目录 一、先理解存证合约的设计要点 二、用 Java SDK 部署和调用(普通方式) 环境准备 编译合约生成 Java 文件 部署合约 调用存证 三、可升级存证合约:为什么需要、怎么实现 为什么普通合约不够用 OpenZeppelin 的 TransparentUpgradeableProxy 方案 部署可升级合…

2026/10/14 3:30:35 阅读更多 →
本体(Ontology)在行业与技术上的发展、演变与平台架构实践

本体(Ontology)在行业与技术上的发展、演变与平台架构实践

1. 本体的基本概念与定位 在计算机科学与企业架构中,本体(Ontology) 是对某一领域共享概念体系的明确、形式化规范说明(Tom Gruber, 1993)。它定义: 概念:类/实体,如「客户」「订单」…

2026/10/14 3:30:35 阅读更多 →
CAXA3D实体设计零件建模演示方法

CAXA3D实体设计零件建模演示方法

现在以该图为例,演示建模方法第一步:制作底座打开CAXA3D实体建模软件,选择制作【3D设计环境】,从右侧【图素】中选择【长方体】,左键单击两下至图像显示如图(即选中图像),右击图像&a…

2026/10/14 3:30:35 阅读更多 →

最新新闻

企业微信消息接入最小可行Demo:Python Flask原生实现接收-解密-响应闭环

企业微信消息接入最小可行Demo:Python Flask原生实现接收-解密-响应闭环

简介:本资源是一个基于Spring Boot的企业微信对接实战示例项目,面向Java后端开发者及企业级应用集成学习者,聚焦解决企业微信消息接收、签名验证与自动回复等核心接入问题。项目以轻量级工程结构呈现,共152个文件,涵盖…

2026/10/14 4:14:49 阅读更多 →
Flutter脚手架鸿蒙化适配:easy_init_cli的工程初始化资产治理实践

Flutter脚手架鸿蒙化适配:easy_init_cli的工程初始化资产治理实践

接手这个任务前,我团队里新开一个 Flutter 项目的标准流程,还是从 Git 仓库拉一个老工程,全局替换包名,然后祈祷 replace 别把注释里的示例代码也改了。这种"复制粘贴式初始化"撑了三年,终于把问题摆到了台面…

2026/10/14 4:14:49 阅读更多 →
Boyer-Moore投票算法:线性时间找多数元素的工业级实现

Boyer-Moore投票算法:线性时间找多数元素的工业级实现

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

2026/10/14 4:14:49 阅读更多 →
开发环境搭建与Git入门:新手必备工具链详解

开发环境搭建与Git入门:新手必备工具链详解

最近带了几位刚接触开发的同学,发现一个很普遍的现象:代码语法学得挺快,但一旦脱离教程,自己装环境、跑项目、提交代码,就完全卡住。问题不是笨,也不是代码写得少,而是开发基础知识里最日常的那…

2026/10/14 4:14:49 阅读更多 →
LeetCode热题100链表专题:虚拟头节点与双指针全解法突破

LeetCode热题100链表专题:虚拟头节点与双指针全解法突破

1. 项目概述:hot100 链表专题到底在考什么先说实话,链表这玩意,每次刷题都觉得“这也不难啊”,但一到面试手写,翻车率奇高。热题100 里的链表专题我前前后后刷了三轮,每一次都有新体会。这个专题说白了一共…

2026/10/14 4:14:49 阅读更多 →
【风电功率预测】【多变量输入单步预测】基于TCN-RVM的风电功率预测研究(Matlab代码实现)

【风电功率预测】【多变量输入单步预测】基于TCN-RVM的风电功率预测研究(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 &#x1f381…

2026/10/14 4:13:48 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →