异常处理三宗罪:吞异常、过度捕获与日志滥用

前言

请添加图片描述

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:https://www.captainbed.cn/z
在这里插入图片描述
请添加图片描述


1. 错误场景复现

场景1:吞掉异常(Silent Swallow)

public void processOrder(Order order) {
    try {
        validate(order);
        saveToDatabase(order);
    } catch (Exception e) { 
        // 吞掉异常,无日志、无处理
    }
}

后果:订单处理失败时无任何痕迹,导致后续流程数据错乱且难以排查。


场景2:捕获过于宽泛的异常

try {
    config = loadConfig();       // 可能抛出IOException
    startService(config);        // 可能抛出IllegalStateException
} catch (Exception e) {          // 捕获所有异常
    if (e instanceof IOException) {
        log.error("配置加载失败"); 
    }
    // 但IllegalStateException未被处理!
}

隐患:将非受检异常(如空指针)与业务异常混为一谈,掩盖核心问题。


场景3:重复日志与堆栈轰炸

try {
    parseJson(data);
} catch (JsonException e) {
    log.error("JSON解析失败: " + e.getMessage());  // 记录1次
    throw new ServiceException("数据处理失败", e);  // 上层又记录堆栈
}

// 上层调用
try {
    service.process(data);
} catch (ServiceException e) {
    log.error("业务处理失败", e);  // 堆栈重复打印
}

问题:同一异常链被多次记录,日志文件体积暴涨,关键信息被淹没。


2. 原理解析

异常处理的核心原则

  1. 明确性:异常应反映具体问题(如NetworkException vs DatabaseException)
  2. 完整性:保留原始异常信息(cause链传递)
  3. 适度性:在能处理的层级捕获,否则抛出

Checked vs Unchecked异常

Checked ExceptionUnchecked Exception
继承关系继承自Exception继承自RuntimeException
处理要求必须显式捕获或声明抛出可选择性处理
适用场景可预见的业务错误(如参数校验)系统级错误(如NPE、越界)

日志滥用引发的性能问题

  • 同步写入磁盘:每个log.error()触发I/O操作
  • 堆栈深度:打印堆栈需遍历线程栈,消耗CPU
  • 日志序列化:异常对象转换为字符串的计算开销

3. 正确解决方案

方案1:精准捕获 + 异常传递

public void processFile(String path) {
    try {
        readContent(path);
    } catch (FileNotFoundException e) {  // 精确捕获
        log.error("文件不存在: {}", path);
        throw new BusinessException("FILE_MISSING", "文件未找到", e); // 封装并传递
    } catch (IOException e) {            // 较宽泛但必要
        log.error("文件读取失败", e);
        throw new BusinessException("IO_ERROR", "系统错误", e);
    }
}
要点:
  • 优先捕获具体异常类型
  • 自定义业务异常携带错误码和上下文
  • 始终传递原始异常(e作为cause)

方案2:全局异常处理器(Spring示例)

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理业务异常(返回结构化的错误信息)
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
        return ResponseEntity.status(500)
            .body(new ErrorResponse(e.getCode(), e.getMessage()));
    }

    // 兜底处理未捕获异常(记录堆栈,但屏蔽细节)
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleUnexpectedException(Exception e) {
        log.error("系统未知错误", e);  // 仅在此处记录完整堆栈
        return ResponseEntity.status(500)
            .body(new ErrorResponse("SYSTEM_ERROR", "服务器繁忙"));
    }
}
优势:
  • 避免Controller层重复try-catch
  • 统一异常响应格式
  • 敏感信息过滤(如不返回SQL异常给前端)

方案3:日志优化策略

try {
    riskyOperation();
} catch (SpecificException e) {
    // 正确做法:携带上下文 + 仅记录一次堆栈
    log.error("操作失败,ID={}, 原因={}", id, e.getMessage(), e); // 最后一个参数传递异常对象
    
    // 避免冗余信息:
    // 错误示例:log.error("失败:" + e); (字符串拼接消耗性能)
}
日志规范:
  • 使用占位符({})而非字符串拼接
  • 仅在最外层记录完整堆栈
  • 敏感信息脱敏(如手机号、密码)

4. 工具与最佳实践

静态分析工具

  1. SonarQube规则:

    • "java:S1166" - 异常应携带详细信息
    • "java:S2221" - 避免捕获Throwable
    • "java:S1452" - 禁止暴露泛型异常
  2. IntelliJ插件:

    • @Nonnull/@Nullable注解辅助空安全
    • 自动检测未被处理的异常

自定义异常设计模板

public class BusinessException extends RuntimeException {
    private final String errorCode;

    public BusinessException(String errorCode, String message, Throwable cause) {
        super(message, cause);  // 传递cause链
        this.errorCode = errorCode;
    }

    // 生成标准化错误响应
    public ErrorResponse toResponse() {
        return new ErrorResponse(errorCode, getMessage());
    }
}
扩展建议:
  • 错误码分类:A-BB-CCC格式(A: 系统, BB: 模块, CCC: 具体错误)
  • 自动生成文档:通过注解关联错误码与解决方案

5. Code Review检查清单

检查项正确做法
是否捕获Throwable?禁止!可能捕获Error(如OOM),导致JVM无法正常恢复
是否在合适层级处理异常?DAO层抛异常,Service层转换,Controller层处理
日志是否重复记录堆栈?确保异常链中每个堆栈只打印一次
自定义异常是否携带错误码?便于前端和监控系统分类处理

6. 真实案例

某支付系统在资金划转时捕获异常但未记录:

try {
    account.transfer(amount);
} catch (InsufficientBalanceException e) {
    // 无日志、无处理
}

后果:用户余额充足却提示扣款失败,客诉量激增。
分析:底层抛出的是NetworkException,但被宽泛的catch (Exception e)拦截后未记录,误判为余额不足。
修复:

  1. 引入自定义异常PaymentException,区分业务与系统异常
  2. 关键路径添加审计日志(含请求ID、账户、金额)
  3. 配置ELK日志系统实时报警

总结

  • 绝不吞掉异常:至少记录日志,必要时传递
  • 精准捕获:避免catch (Exception e)的懒惰写法
  • 日志是黄金:在关键位置记录上下文,但避免冗余
  • 统一管理:全局处理器 + 自定义异常 = 优雅的错误交互

下期预告:《并发编程初学者的噩梦:线程安全与锁机制误区》——从死锁到数据竞争,彻底拆解多线程开发的隐蔽陷阱。

联系作者

职场经验分享,Java面试,简历修改,求职辅导尽在科技泡泡
思维导图面试视频合集
在这里插入图片描述
在这里插入图片描述

3大神器+2步代码!C# WinForm如何让异常‘秒变’纸老虎?——附全局捕获代码+日志急救包! 🔥关注墨瑾轩,带你探索编程的奥秘!🚀🔥超萌技术攻略,轻松晋级编程高手🚀🔥技术宝库已备好,就等你来挖掘🚀🔥订阅墨瑾轩,智趣学习不孤单🚀🔥即刻启航,编程之旅更有趣🚀。 阅读详情

相关推荐

Flask优雅的错误降级:自定义 HTTP 错误页与全局异常捕获处理器

本文深入探讨了Flask框架中的错误处理机制,揭示了默认错误页面的三大缺陷(体验差、安全隐患、架构混乱),并提出了一套完整的解决方案。文章首先分析了HTTP异常与普通异常的本质区别,随后详细讲解了如何通过装饰器自定义错误页面(支持HTML/JSON动态响应),并构建全局异常捕获系统作为"安全网"。最后介绍了企业级开发中如何建立统一的自定义异常体系,实现内外有别的优雅降级:对用户展示友好界面,同时将详细错误记录到日志系统。这套机制能有效提升Web应用的健壮性和用户体验。

数据知道的博客 1341

C#异步代码的3大陷阱:async/await如何让异步变“同步”?为何90%开发者踩坑?

《C#异步编程救火指南:从死锁陷阱到性能优化》揭示了90%开发者面临的"伪同步"危机,通过金融系统真实案例展示误用异步代码导致的性能灾难。文章对比同步与异步的本质差异,解剖async/await的三大误区(死锁、异常丢失、上下文捕获),提供IL反编译等诊断黑科技。解决方案涵盖熔断器模式、优雅降级策略,并推荐dotMemory等急救工具。最后提出"监控即正义"的维护哲学,强调预防性措施的重要性,帮助开发者从救火队员蜕变为架构大师。(149字)

java专栏 782

Arrays.asList()的3大坑:凌晨三点报警call!90%的Java工程师都踩过的陷阱,传统转换VS墨式急救术,为何企业避而不谈?

摘要: 《墨瑾轩技术揭秘:Arrays.asList()的陷阱与系统维护哲学》深入剖析Java中Arrays.asList()的三大致命缺陷:固定大小导致的操作异常、与原数组的隐性绑定及无法扩容问题,通过真实案例揭示90%企业系统故障源于历史代码隐患。文章提出“监控-日志-预案”三位一体的墨氏维护法则,提供熔断降级、热更新等急救方案,并推荐dotMemory、ELK等诊断工具,强调“预防优于救火”的工程师思维。全文以幽默犀利的“墨式语言”警示开发者避免自欺欺人的代码实践。(149字) 关键词: Arrays

java专栏 356

apollo.zip

最新apollo官方开源包,源码学习;最新apollo官方开源包,源码学习

【AI助力科研】不看一篇文献,手把手教你用chatGPT写文献综述!

这篇文章以【AI助力科研】为题,介绍了如何利用ChatGPT编写文献综述的方法。作者通过详细的指导,展示了不需要阅读大量文献,只需依靠ChatGPT模型即可完成文献综述的过程。该方法旨在提高科研工作者的效率,减少阅读负担,并且能够生成具有逻辑性和准确性的文献综述。

研究方向//图像视频处理/信号算法/红外无损检测/数值仿真/机器学习/优化算法/python/matlab 2万+

文本→图像→视频:AIGC跨模态转换全流程解析

本文旨在系统性地解析AIGC领域中跨模态内容生成的技术原理和实现方法,特别聚焦于从文本到图像再到视频的完整转换流程。跨模态表示学习的基本原理文本到图像生成的核心技术图像到视频转换的关键算法端到端跨模态生成系统的构建方法实际应用场景和性能优化策略背景介绍:建立基本概念和知识框架核心概念:解析跨模态转换的关键技术和原理算法细节:深入讲解文本到图像、图像到视频的核心算法数学模型:提供必要的数学基础和公式推导项目实战:通过完整案例展示实现过程应用场景:探讨技术的实际应用可能性。

AI天才研究院 1355

torchsparse安装

torchsparse安装以及安装失败原因分析和解决

AI_BING_的博客 1万+

Flink实战(101):双流join(三)双流中实现Inner Join、Left Join与Right Join

声明:本系列博客是根据SGG的视频整理而成,非常适合大家入门学习。 《2021年最新版大数据面试题全面开启更新》 简介 之前文章中提到JoinedStream与CoGroupedStream,例如下列代码: dataStream.join(otherStream) .where(0).equalTo(1) .window(TumblingEventTimeWindows.of(Time.seconds(3))) .apply { ... } 由于joinedStream与

微信搜:import_bigdata,大数据领域硬核原创作者 739

基于通义千问2.5-7B-Instruct的技术深度解析

通义千问2.5-7B-Instruct是一个基于Transformer架构的大型语言模型,具有70亿参数。它通过自注意力机制和前馈神经网络处理输入序列,能够捕捉长距离依赖关系,适用于多种NLP任务。微调是指在预训练模型的基础上,使用特定任务的数据进行进一步训练,以使模型更好地适应特定任务。微调可以显著提高模型在特定任务上的性能。通义千问2.5-7B-Instruct是一个强大的语言模型,适用于多种NLP任务。通过深入理解其架构、微调方法和核心代码,可以更好地应用于实际场景。

zhangjiaofa的专栏 1570

MATLAB - 浮动基座机器人的逆运动学

本例演示如何解决以浮动底座为模型的机器人的逆运动学问题。浮动底座机器人可以在空间中自由平移和旋转,具有六个自由度。浮动基座机器人的逆运动学问题适用于空间应用,即使用安装在浮动和致动基座上的机械臂在空间操纵物体,也适用于躯干和腿部可在空间自由移动的行走机器人。请注意,刚体树对象表示具有固定基体的刚体树。这意味着刚体树的基体在世界中具有固定的位置和方向。您可以在基体上附加一个 “浮动 ”类型的刚体关节,为具有浮动基体的机器人建模,以用于动态模拟等某些应用。

机器人控制与仿真应用 2440

手把手教你学Simulink——基于自动驾驶辅助系统的场景实例:车道保持辅助系统(LKA)的仿真与实现

本文通过Simulink实例讲解车道保持辅助系统(LKA)的实现方法。首先介绍LKA从预警到主动控制的技术演进,阐述其系统架构、核心组件和工作原理。重点演示基于自行车模型的车辆横向动力学建模过程,以及使用线性二次调节器(LQR)设计横向控制器的详细步骤。教程包含四个仿真场景测试,分析关键指标如控制精度、舒适性和响应性。最后探讨LKA技术的发展方向,强调人机协同的安全理念。该实例为自动驾驶辅助系统开发提供了完整的Simulink实践方案。

MHD0815的博客 182

MIKE水动力笔记17_MIKE文件转shp、统计每个单元格的面积

MIKE的工具箱中自带一个转shp的工具,然后可以拖进ArcGIS中很方便的统计每个单元格的面积和每个网格点的水深。

晏长街的博客 4153

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: 浅拷贝与深拷贝:对象复制的隐藏风险
下一篇: 职业习惯养成:如何避免成为“救火队员”?
雪碧有白泡泡
雪碧有白泡泡 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域
博客等级 码龄5年 1万+粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪碧有白泡泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值