本篇不打算冗长介绍各种异常,只写出通用的应该遵循的异常处理规范(个人理解,如有错误欢迎指正)
1. 检查异常(checked exception),通常见到的有SQLException,IOException,InterruptedException,ConnectTimeOutException,ClassNotFoundException等等。这些异常往往是由于数据库,网络和资源问题导致的,不是程序本身正常运行的情况。这些异常被认为是可以恢复的,比如网络异常可以通过重试几次有可能就解决了。这类异常要求程序员必须捕获并作处理,程序员的具体处理还是要根据业务情况而定。
非检查异常(unchecked exception),常见的有NullPointerExceprion,OutOfBoundsException,IllegalArgumentExcetion,NumberFormatException等(一般这类异常继承自RuntimeException),这些一般是程序内部的,由于数据格式的问题产生的非正常情况,理论上是不应该发生的,而我们也的确可以通过某种方式避免一些非检查异常(数组长度判断,非空判断等)。非检查异常不要求程序员必须捕获,有时候函数头会指明抛出的非检查异常(但是不要求一定捕获),有时候只在api的注释里面说明可能抛出的异常。这个是api设计的不一致问题。
java区分这两种异常,在一些时候显得没那么必要,检查异常要求强制try-catch,以及直接在函数头上声明出来,会让代码很难看。因此一般项目中使用的都是非检查异常,有些底层的检查异常最后也会被转译为非检查异常。非检查异常不会污染接口,但是还是需要处理。
2. 不要使用异常做流程控制
比如不对除数做判断,当发生异常时,再得出除数等于0或返回失败。或者当某段代码异常了表示进入某个分支,没有异常就走另外一分支。或者通过异常跳出循环。因为异常的try-catch是消耗资源的做法,异常针对的是非正常情况,不应该与正常的业务代码混用。
3. 异常如果能处理就内部处理了,如果不能处理就抛给调用者,而不应该catch了异常,然后打印了堆栈信息就完了。
异常能处理,比如发生了检查异常,我们可以重试多次,如果多次仍然失败,就抛出;或者说一段代码异常,没有更高一级的调用者能够处理了,再抛出就抛给虚拟机了,此时要处理它;或者说某段代码异常就可以认定这个任务失败了,这时就可以处理之做状态变更。
catch了异常不处理,只打印,导致的问题就是调用者正常调用了这个方法,也正常返回了,觉得应该没有问题了,但是实际上方法内部异常了。这时候调用者拿到错误的数据就会产生非常严重的后果。虽然有日志打印,但是实际上业务上认为成功了,除非任务失败,否则如果没有人检查日志就不会有人发现这段错误。
4. 保留异常现场
异常发生时,如果确定要catch处理它,除了记录堆栈信息(使用日志的项目中,需要用logger记录,而不要e.printStackTrace(),此外,我见过只记录e.getMessage()的,发生错误之后,我只能看到一个空指针异常,而看不到哪一行空指针了),还要保留异常现场。也就是说发生异常时正在做什么操作,操作的输入参数输出参数是怎样的(日志格式强烈建议规范化,不然不统一的格式看起来很难受)。方便从参数中发现异常原因,排查问题。
5. 不要有过大的try块
try-catch是比较消耗资源的做法(不清楚JVM的实现,有机会再研究,但是直观地看,程序要保留异常堆栈信息,监控代码是否异常,应该不是轻松的事情),
所以只针对可能抛出异常的代码try-catch,try-catch中不应该有太多无关操作。比如异常做状态修改,完全可以用一个boolean值在catch完之后再做记录。
6. 不要从头到尾只用一种异常
不要只用exception或者RuntimeException处理所有的异常。程序当中应该区分不同模块和不同层的异常,比如系统异常业务异常,dao异常service异常,方便出错的时候定位问题。另外,对异常可以使用编码来记录异常类型和异常说明。这样可以用一个类/配置文件,来对异常进行管理。如果要修改异常的表示方式或者文字说明,也不用每个类里面乱找。将一些一些api异常或者底层的异常,转移为业务异常(catch之后new一个业务异常),也是经常有的。
7. 对try块中申请的资源,应该要在finally中释放掉,java7提供了try(声明资源语句),但总之资源都应该保证退出方法时被释放。
8. 记住异常是非正常情况,如果能够避免就尽量避免,异常只是一种保障机制,不代表我们可以无视业务逻辑和算法,随便写,写完catch就行了。
对于一些可以规避的异常(空指针,非法数字格式,下标越界等),可以通过程序判断来避免。对于web的控制层异常,需要转译异常,跳转到错误页面,或者是提示前台【内部错误】,不要将堆栈信息也显示到前台。
---------------------------以上----------------------------