java如何处理异常:优先捕获具体异常再兜底捕获通用异常
刚入行写业务代码时,完全搞不懂java如何处理异常,只觉得try-catch是万能模板,不管什么报错,直接把代码块套进去,程序不崩就算完成任务。那时候写的代码异常处理全是摆设,线上出问题的时候,日志空空如也,根本查不到报错根源,好几次迭代都因为这个被测试打回。
最离谱的一次是做用户支付回调接口,所有可能出问题的代码只套了一个空的try-catch块,catch里没有任何日志打印,也没有任何补救逻辑。当时想着只要拦截住异常,服务就不会宕机,完全没考虑不同异常对应的问题场景,空指针、参数格式错误、网络超时这些完全不同的问题,全都被同一个空白捕获块吞掉了。上线第二天就出了故障,用户支付成功但订单状态一直未更新,后台没有任何报错记录,排查了整整三个小时,一点点拆解代码、逐行测试运行逻辑,才找到是参数解析异常被静默吃掉,白白浪费了大量的运维和迭代时间。
那次故障之后,才开始正视异常处理的问题。
之前一直误以为,异常处理的目的就是阻止程序崩溃。反正只要程序不挂,就算处理到位了。抱着这个想法写了大半年代码,产出的全是无效的异常捕获逻辑,看似稳妥,实则埋了一堆隐性bug,平时测试测不出来,一上线就随机爆发。
折腾好久才搞明白,java异常处理根本不是为了单纯保程序运行,核心是精准定位问题、留存问题线索、针对性修复业务问题。最忌讳的新手操作就是直接捕获顶级的Exception,一把包揽所有异常,看似省事,实则直接屏蔽了所有报错细节,线上排查问题的效率直接归零,相当于给自己的代码蒙上了一层黑盒。
后来跟着老开发调整写法,终于摸到了实操的门道。写异常捕获时,优先针对代码大概率会出现的具体异常单独捕获,比如对象未初始化引发的空指针异常、时间字符串转换失败的解析异常、文件读取接口调用的IO异常,每一种异常都搭配专属的处理逻辑。空指针就提前初始化默认参数规避报错,参数解析失败就向前端返回明确的参数错误提示,IO异常就执行重试机制同时打印详细报错日志。
真正落地之后才发现,这种针对性处理的优势特别明显,线上再出现问题,日志会清晰打印出异常类型、报错行数、具体错误信息,不用再盲目的全局排查问题。而且不同异常的处理逻辑分开之后,业务容错性也变好了,不会因为一个小的参数错误直接导致整个接口卡死,也不会把所有报错都统一返回未知错误,用户的使用体验也得的到了优化,项目的稳定性肉眼可见的提升。
不会彻底摒弃通用异常捕获,只是把它放在所有具体捕获逻辑的最后做兜底。所有预判到的常规异常处理完毕后,最后用Exception拦截所有没预估到的未知问题,既保证了已知问题精准处理、方便快速修复,又能避免未知异常直接导致程序终止,这是实际开发里最平衡、最好用的处理方式。
那天改完项目所有接口的异常处理逻辑,逐条测试通过后,盯着控制台清晰完整的报错日志,终于不用再面对空白日志束手无策。