try{ int i = 0; while(true) range[i++].climb(); }catch(ArrayIndexOutOfBoundsException e){ }
企图经过异常来终止循环,是对异常的误用,异常是针对不正常状况才使用的。上面的基于异常模式运行上,比正常的模式在性能上慢不少,2倍或以上。java
对于API的设计也是如此,不要强迫它的客户端为了正常的控制流而使用异常程序员
通常的处理方法有两种:数组
若是对象的状态是线程安全的话,推荐使用,例如安全
for(Iterator<Foo> i = collection.iterator();i.hasNext();){ Foo foo = i.next(); }
这里的hasNext就是状态监测并发
若是使用异常的话,客户端就得这样性能
try{ Iterator<Foo> i = collection.iterator(); while(true){ Foo foo = i.next(); } }catch(NoSuchElementException e){ }
对于iterator这种状况,返回null是可行的。从性能角度看,返回可识别的值这种方法较好。学习
若是指望调用者可以适当的恢复,对于这种状况,应该使用受检异常。this
API设计者让API用户面对受检异常,以强制用户从这个异常条件中恢复过来,用户能够忽视这样的强制要求,只需捕获异常并忽略便可,但这每每不是一个好方法。spa
一般用来代表程序错误,好比precondition violation,前提违例,即API的客户没有遵循API规范创建的约定线程
受检异常本质上没有给程序员提供任何好处,反而须要付出努力,使得程序变得复杂。
把受检异常变为非受检异常的一个方法,就是把抛出异常的方法拆分为两个,第一个方法返回boolean,代表是否应该抛出异常。例如iterator的hasNext,这种重构并不老是恰当的,可是若是用的合理,API使用起来就更加舒服。固然这种等同于状态监测的方法,若是线程不安全的话,不推荐这种重构。
A、使得你的API更加易于学习
B、可读性更好一些
C、异常类越少,则内存印迹越小,装载这些类的开销也就越少
A、IllegalArgumentException
传递参数不合适的时候
B、IllegalStateException
由于接受对象的状态而使得调用非法,好比在未初始化完毕以前使用对象
C、ConcurrentModificationException
并发修改
D、UnsupportedOperationException
好比对只支持追加操做的list进行删除操做
若是方法抛出的异常与它所执行的任务没有明显的联系,这种情形将会使得人们不知所措,特别是当方法传递由低层抽象抛出的异常时,这除了让人困惑以外,也让实现细节污染了更高层的API。
为了不这个问题,须要进行异常转义(exception transaction),即更高层的实现应该捕获低层的异常,同时抛出能够按照高层抽象进行解释的异常。好比List<E>中的get方法,对低层AbstractSequentialList类抛出的异常进行转义。
public E get(int index){ ListIterator<E> i = listIterator(index); try{ return i.next(); }catch(NoSuchElementException e){ throw new IndexOutOfBoundsException("Index:"+index); } }
异常链是异常转义的特殊形式,若是低层的异常对于调试致使高层异常的问题很是有帮助的话,使用异常链就很合适。大多数标准的异常都有支持链的构造器(Throwable cause),对于没有支持链的异常,能够利用Throwable的initCause方法设置缘由。
尽管异常转译与不加选择地从低层传递异常的作法有所改进,可是它也不能被滥用。处理来自低层的异常的最好的作法就是,在调用低层方法以前,先把来自高层的参数校验成功,从而尽量避免在低层抛出异常。
同时记录抛出该异常的条件
这样没有给调用者任何准确的指导信息
即异常的toString方法,通常包含异常的类名以及异常细节
好比IndexOutOfBoundsException的异常细节应该包含下界、上界以及没有落在界内的下标值。
能够定义一个构造器,抛出异常时,传入这些必要的参数,而后自动产生细节信息。
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index){ super("lower bound:"+lowerBound+ ",upper bound:"+upperBound+ ",index:"+index); this.lowerBound = lowerBound; this.upperBound = upperBound; this.index = index; }
异常细节通常是让程序员去分析失败缘由的
即失败的方法调用应该使对象保持在被调用以前的状态。
A、设计一个不可变对象
B、对于可变对象,在执行操做以前检查参数有效性
扩展:尽量调整计算过程的顺序,使得可能失败的部分发生在对象被修改以前
C、编写一段恢复代码(recovery code)
不是很经常使用呢,能够经过拦截处理
D、拷贝对象进行操做,成功以后用其替代对象的内容
好比Collections.sort在执行排序以前,先把输入列表转到一个数组中,以便下降在排序的内循环中访问元素所须要的开销,另一个考虑就是当排序失败的时候,也能保证输入列表保持原样。
总之,做为方法规范的一部分,产生的任何异常都应该让对象保持在该方法调用以前的状态。
(1)空的catch块会使异常达不到应有的目的
(2)不重复地记录异常,能够在必要的时候调查异常缘由