乐趣区

Java异常处理12条军规

摘要:简单实用的建议。

原文:Java 异常处理 12 条军规

公众号:Spring 源码解析

Fundebug 经授权转载,版权归原作者所有。
在 Java 语言中,异常从使用方式上可以分为两大类:

CheckedException
UncheckedException

在 Java 中类的异常结构图如下:

可检查异常需要在方法上声明,一般要求调用者必须感知异常可能发生,并且对可能发生的异常进行处理。可以理解成系统正常状态下很可能发生的情况,通常发生在通过网络调用外部系统或者使用文件系统时,在这种情况下,错误是可能恢复的,调用者可以根据异常做出必要的处理,例如重试或者资源清理等。
非检查异常是不需要在 throws 子句中声明的异常。JVM 根本不会强制您处理它们,因为它们主要是由于程序错误而在运行时生成的。它们扩展了 RuntimeException。最常见的例子是 NullPointerException 可能不应该重试未经检查的异常,并且正确的操作通常应该是什么都不做,并让它从您的方法和执行堆栈中出来。在高执行级别,应记录此类异常。
Error 是最为严重的运行时错误,几乎是不可能恢复和处理,一些示例是 OutOfMemoryError,LinkageError 和 StackOverflowError。它们通常会使程序或程序的一部分崩溃。只有良好的日志记录练习才能帮助您确定错误的确切原因.

在异常处理时的几点建议:
1. 永远不要 catch 中吞掉异常,否则在系统发生错误时,你永远不知道到底发生了什么
catch (SomeException e) {
return null;
}
2. 尽量使用特定的异常而不是一律使用 Exception 这样太泛泛的异常
public void foo() throws Exception { // 错误的做法}
public void foo() throws MyBusinessException1, MyBusinessException2 { // 正确的做法}
一味的使用 Exception, 这样就违背了可检查异常的设计初衷,因为调用都不知道 Exception 到底是什么,也不知道该如何处理。捕获异常时,也不要捕获范围太大,例如捕获 Exception,相反,只捕获你能处理的异常,应该处理的异常。即然方法的声明者在方法上声明了不同类型的可检查异常,他是希望调用者区别对待不同异常的。
3. Never catch Throwable class
​永远不要捕获 Throwable, 因为 Error 也是继承自它,Error 是 Jvm 都处理不了的错误,你能处理?所以基于有些 Jvm 在 Error 时就不会让你 catch 住。
4. 正确的封装和传递异常 **
不要丢失异常栈,因为异常栈对于定位原始错误很关键
catch (SomeException e) {throw new MyServiceException(“Some information: ” + e.getMessage()); // 错误的做法 }
一定要保留原始的异常:
catch (SomeException e) {throw new MyServiceException(“Some information: ” , e); // 正确的打开方式 }
5. 要打印异常,就不要抛出,不要两者都做
catch (SomeException e) {
LOGGER.error(“Some information”, e);
throw e;
}
这样的 log 没有任何意义,只会打印出一连串的 error log,对于定位问题无济于事。
6. 不要在 finally 块中抛出异常
如果在 finally 中抛出异常,将会覆盖原始的异常, 如果 finally 中真的可能会发生异常,那一定要处理并记录它,不要向上抛。
7. 不要使用 printStackTrace
要给异常添加上有用的上下文信息,单纯的异常栈,没有太大意义
8. Throw early catch late
异常界著名的原则,错误发生时及早抛出,然后在获得所以全部信息时再捕获处理. 也可以理解为在低层次抛出的异常,在足够高的抽象层面才能更好的理解异常,然后捕获处理。
9. 对于使用一些重量级资源的操作,发生异常时,一定记得清理
如网络连接,数据库操作等,可以用 try finally 来做 clean up 的工作。
10. 不要使用异常来控制程序逻辑流程
我们总是不经意间这么做了,这样使得代码变更丑陋,使得正常业务逻辑和错误处理混淆不清;而且也可能会带来性能问题,因为异常是个比较重的操作。
11. 及早校验用户的输入
在最边缘的入口校验用户的输入,这样使得我们不用再更底层逻辑中处处校验参数的合法性,能大大简化业务逻辑中不必要的异常处理逻辑;相反,在业务中不如果担心参数的合法性,则应该使用卫语句抛出运行时异常,一步步把对参数错误的处理推到系统的边缘,保持系统内部的清洁。
12. 在打印错误的 log 中尽量在一行中包含尽可能多的上下文
LOGGER.debug(“enter A”);LOGGER.debug(“enter B”); // 错误的方式
LOGGER.debug(“enter A, enter B”);// 正确的方式

退出移动版