- 提倡异常封装
异常封装有三方面的优点:
1、提高系统的友好性
异常的阅读者分为两类:开发人员和用户。开发人员查找问题,需要打印出堆栈信息,而用户则需要了解具体的业务原因,比如文件太大、不能同时编写文件等。
2、提高系统的可维护性
3、解决Java异常机制自身的缺陷
一个方法抛出多个异常:绝对可能出现,例如Web界面注册时,展现层依次把User对象传递到逻辑层,Register方法需要对各个Field进行校验并注册,例如用户名不能重复,密码必须符合密码策略等,不要出现用户第一次提交时系统提交时系统提示“用户名重复”,在用户修改用户名再次提交后,系统又提示“密码长度少于6位”的情况,这种情况模式下的用户体验非常糟糕,最好的解决方法就是封装异常,建立异常容量,一次性的对User对象进行校验,然后返回所有的异常。
- 采用异常链传递异常
设计模式中有一个模式叫做责任链模式(Chain of Reponsibility),他的目的是将多个对象连成一条链,并沿着这条链传递该请求,直到有对象处理他为止,异常的传递处理也应该采用责任链模式。
对于异常正确的做法是先封装,然后传递,过程如下:
1、把FileNotFoundException封装为MyException。
2、抛出到逻辑层,逻辑层根据异常代码(或者自定义的异常类型)确定后续处理逻辑,然后抛出到展现层。
3、展现层自行决定要展现什么,如果是管理员则可以展现低层次的异常,如果是普通用户则展示封装后的异常。
异常需要封装和传递,我们在进行系统开发时不要“吞噬”异常,也不要“赤裸裸”的抛出异常,封装后再抛出,或者通过异常链传递,可以达到系统更健壮、友好的目的。
- 受检异常尽可能转化为非受检异常
受检异常不足的地方:
1、受检异常使接口声明脆弱
2、受检异常使代码的可读性降低
3、受检异常增加了开发工作量
- 不要在finally块中处理返回值
- 不要在构造函数中抛出异常
构造函数抛出错误是程序员无法处理的
构造函数不应该抛出非受检异常
1、加重了上层代码编写者的负担
2、后续代码不会执行
构造函数尽可能不要抛出受检异常
1、导致子类代码膨胀
2、违背了里氏替换原则
3、子类构造函数扩展受限
- 使用Throwable获得栈信息
JVM在创建一个Throwable类及其子类时会把当前线程的栈信息记录下来,以便在输出异常时准确定位异常原因。
在出现异常时(或主动声明一个Throwable对象时),JVM会通过fillInStackTrace方法记录下栈帧信息,然后生成一个Throwable对象,这样我们就可以知道类间的调用顺序、方法名称及当前行号等了。
- 异常只为异常服务
- 多使用异常,把性能问题放一边
Java的异常处理机制确实比较慢,这个“比较慢”是相对于诸如String、Integer等对象来说的,单单从对象的创建上来说,new一个IOException会比String慢5倍,这从异常的处理机制上也可以解释:因为他要执行fillInStackTrace方法,要记录当前栈的快照,而String类则是直接申请一个内存创建对象,异常类慢一筹也就在所难免了。
而且,异常类是不能缓存的,期望预先建立大量的异常对象以提高异常性能也是不现实的。
难道异常的性能问题就没有任何可提高的方法了?确实没有,但是我们不能因为性能问题而放弃使用异常,而且经过测试,在JDK1.6下,一个异常对象创建的时间只需要1.4毫秒左右(注意是毫秒,通常一个交易处理是在100毫秒左右)。