Java异常处理的最佳实践
为什么要有最佳实践
我们在写程序是不可避免的要对代码进行异常处理,但是有时对异常的处理会使我们的程序变的更加糟糕,这是我们所不想看到的。所以,我们再进行异常处理时需要遵循一定的套路,来降低异常处理对我们程序的影响。
异常产生的原因
一般来说,java中的异常会产生于一下三种情况:
- 编程错误导致的异常,例如NullPointerException 和 IllegalArgumentException。这类的异常一旦发现,必须通过修改代码来解决。
- 资源错误导致的异常,例如网络资源socket、文件资源等。这类的异常出现时往往会以记录日志,挂起程序或者进行重试来作为解决方法。
- 输入数据导致的异常,例如输入的JSON数据与程序中所指定的规范不一致,由此产生数据无法正常解析所抛的异常。这类的异常发生时,需要逐步调试程序或输入的数据。
Java异常的分类
java中定义了两种异常:
- Checked Exception:该类异常直接继承自Exception类。在调用抛出checked exception的API时,必须使用catch进行异常处理或者将该异常throw出去,否则代码不能通过编译。
- Unchecked Exception:该类异常继承自Runtime Exception,Runtime Exception也继承自Exception类,该类异常不需要显式的进行处理就可以进行编译。
进行异常处理的最佳实践
异常选择
对于程序可以进行处理的异常,一般选择通过Checked Exception进行抛出,程序在catch住一个异常之后,可以选择记录日志或者恢复数据或者进行别的操作。
对于程序无法处理的异常,一般抛出Unchecked Exception,将异常抛往更高层的逻辑进行处理,当然Unchecked Exception也是可以进行catch的,当所有的代码均没有对该异常进行处理时,该异常会被交给应用容器进行处理。
在异常处理中释放资源
在各种IO中,一般都会有异常处理,再进行Checked Exception处理时一般在finally中关闭资源。
SQLException一般会被转换成Unchecked Exception
数据库异常一般来说都是连接断掉或者是没有权限等问题,这些在代码里面没法进行处理,因此应该抛出Unchecked Exception。对于某些数据库异常,我们虽然无法解决根本问题,但是仍然是需要进行处理的,比如插入数据是失败,我们应该在代码中捕获该异常,并将原始数据记录下来。
不要使用异常作流程控制
生成栈回溯是非常昂贵的,栈回溯的价值是在于调试。在流程控制中,栈回溯是应该避免的,因为客户端仅仅想知道如何继续。
使用异常进行业务数据校验
对于需要用户输入有要对其进行业务性校验的需求,可以在业务校验失败时抛出自定义异常来告知失败的原因。
例如:在登陆功能中,如果身份校验失败,可以抛出相应的异常到控制器,然后有控制器进行数据输出。
不要忽略异常
针对大多数异常来说,不要使用空的catch块来处理异常。
尽量不要捕捉高级别的异常
高级别的异常会将低级的异常信息吃掉,因此再进行处理时应该尽量捕捉具体的异常类型。
比如,使用Exception作为catch的对象就是一个极端的例子,任何情况下都不要catch Exception这个类,因为这样做毫无意义。
只处理一次
我看到过有人在写代码时catch到了一个异常,然后logger.error(e)进行处理,然后又把这个异常throw出去了,这会导致该异常被处理多次,给拍错人为的增加难度,既然决定将异常抛出就不要进行日志的输出了,但是如果想保存当前变量的状态仍然是可以的,不过这样做,throw这个异常就显得意义不大了。