第57条:只针对异常的情况才使用异常
try {
int i = 0 ;
while (true ){
range[i++].climb();
}catch (ArrayIndextOutOfBoundsException e){
}
}
这段代码可以利用catch来终止无限循环,但这段代码有一下几个缺点:
1.模糊了代码的意图
2.把代码放在try-catch快中阻止本来可能要执行的某些特定优化
3.如果出现了不相关的Bug,这个模式会掩盖这个Bug
所以:异常应该只用于异常的情况下;他们永远不应该用于正常的控制流
对API设计的启发:设计良好的API不应该强迫它的客户端为了正常的控制流而使用异常
如果类具有“状态相关”的方法,也应该有个单独的“状态测试”方法。
例如:Iterator接口有个“状态相关”的next方法,和相应的“状态测试”方法hasNext
如果没有这个hasNext方法,客户端将被迫改用下面的做法:
try {
Iterator<Foo> i = collection.iterator();
while (true ){
Foo foo = i.next()
}catch (NoSuchElementException e){
}
}
另一种类似的做法是:如果"状态相关的"方法被调用时,该对象处于不适当的状态中,他就会返回一个可识别的值,如null。
一些会影响这两种方法选择的因素:调用”状态测试“方法和调用"状态相关"方法之间状态是否会改变以及同步情况,”状态测试“方法和”状态相关“方法的重复度
第58条 对可恢复的情况使用受检异常,对编程错误使用运行时异常
三种throwable:受检的异常(checked exception),运行时异常(run-time exception),错误(error)
异常选择原则:
1.对可恢复的情况使用受检异常
2.对编程错误使用运行时异常
3.最好不要在实现任何新的Error子类
对于受检异常,提供一些辅助方法尤其重要
第59条 避免不必要地使用受检的异常
如果发现某个受检异常不必要(如根本恢复不过来),可以用这种重构方法:
try{
obj.action(args);
}catch(TheCheckedException e){
//Handle exceptional condition
...
}
重构为
if(obj.actionPermitted(args)){
obj.action(args);
}else{
//Handle exceptional condition
...
}
这种方法跟57条中的状态测试方法本质上是一样的,所以也有着同样的问题:
1.缺少外部同步的情况下被并发访问,会被外界改变状态
2.actionPermitted方法如果必须重复action的方法,性能就不高
第60条 优先使用标准的异常
IllegalArgumentException-->非null的参数值不正确
IllegalStateException-->对于方法调用而言,对象状态不合适
NullPointerException-->在禁止使用null的情况下参数值为null
IndexOutOfBoundsException-->下标参数值越界
ConcurrentModificationException-->在禁止并发修改的情况下,检测到对象的并发修改
UnsupportedOperationException-->对象不支持用户请求的方法
第61条 抛出与抽象相对应的异常
如果方法抛出的异常与它所执行的任务没有明显的联系,如方法传递由低层抽象抛出的异常会有两点缺点:
1.让人不知所错
2.如果高层的实现在后续的发行版发生改变,它所抛出的异常也可能跟着发生变化,从而破坏现有的客户端程序
解决方法是异常转译:更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常
一种特殊的异常转译是异常链,如果低层的异常对于调试导致高层异常的问题非常有帮助,就可以使用异常链。
异常链:低层的异常被传到高层的异常,高层的异常提供访问方法来获得低层的异常
第62条 每个方法抛出的异常都要有文档
第63条 在细节消息中包含能捕获失败的信息
第64条 努力使失败保持原子性
一般而言,失败的方法调用应该使对象保持在被调用之前的状态。具有这种属性的方法被称为具有失败原子性.这种原子性可以防止对象处在不一致的状态中.
第65条 不要忽略异常
不要用空的catch块