第57条 只针对异常的情况才使用异常
异常应该只用于异常的情况下,永远不应该用于正常控制流
设计良好的api不应该强迫它的客户端为了正常的控制流而使用异常
可以使用“状态测试方法”或“可识别的返回值方法”避免异常滥用。
“状态测试方法”
当类具有“状态相关”的方法,即只有在特定的不可预测的条件下才可以被调用的方法,这个类应该设置单独的“状态测试”方法,用来指示是否可以调用这个状态相关的方法。
最典型的例子就是iterator中hasNext()方法。
并发场景下“状态测试”方法有可能出现状态不一致的情况
- “返回值方法”
当“状态相关”的方法被调用时,当该对象处于不适当的状态时,返回一个可识别的值,如null。
如果单独的“状态测试”必须重复“状态相关”方法的工作,从性能角度考虑,就应该使用可被识别的返回值
如果其他条件一样,“状态测试”方法优于“返回值”方法,因为前者可读性更强。
第58条 对于可恢复的情况使用受检异常,对于编程错误使用运行时异常
Java三种可抛出结构:受检异常(checked Exception)、运行时异常(runtime Exception)、错误(error)。
- 受检异常:期望调用者能够适当地恢复
- 运行时异常:表明编程错误
第59条 避免不必要地使用受检异常
过分使用受检异常会让api使用起来变得不方便
把受检异常变成未受检异常的方法:把抛异常的方法分成两个方法,一个返回boolean表示是否应该抛异常
1 | //invocation with checked exception |
改为
1 | //invocation with state-testing method and unchecked exception |
优先使用标准的异常
好处:
- 让API更加易于学习和使用
- 可读性会很好
- 异常类少,意味着内存印迹(footprint)越小,装载这些类的时间开销越少
常用异常
异常 | 使用场合 |
---|---|
IllegalArgumentException | 非null的参数值不正确 |
IllegalStateException | 对于方法调用而言,对象状态不合适 |
NullPointerException | 在禁止使用null的情况下参数值为null |
IndexOutOfBoundsException | 下标参数值越界 |
ConcurrentModificationException | 在禁止并发修改的情况下,检测到对象的并发修改 |
UnsupportedOperationException | 对象不支持用户请求的方法 |
第61条 抛出与抽象相对应的异常
底层异常经过传播,当高层方法捕捉异常后会出现异常与任务没有明显联系的情况,解决方案:
- 异常转译
catch底层异常后new一个新的对应的高层异常 - 使用异常链
使用新异常接收cause的构造器构建新异常
第62条 每个方法抛出的异常都要有文档
如果一个方法抛出多个异常类,不要抛出异常类的超类,如“throws Exception”
文档自觉吧。。。
第63条 在细节消息中包含能捕获失败的信息
异常的细节信息应该包含“对该异常有贡献”的参数和域的值,例如IndexOutOfBoundsException异常的细节消息应包含下界、上界以及没有落在界内的下标值
在异常构造器中引入造成失败的参数
第64条 努力使失败保持原子性
失败的方法调用应该使对象保持在被调用之前的状态
实现方法:
- 设计一个不可变对象,如果操作失败,可能会阻止创建新对象,永远也不会使已有对象处于不一致的状态中。
- 在可变对象上获得失败原子性的方法:在执行操作之前进行参数有效性检查
- 调整计算顺序,把可能失败的操作放在对象状态修改操作之前
- 写恢复代码,不常用
- 在临时拷贝上执行操作,在用临时拷贝对象内容代替对象内容
第65条 不要忽略异常
不要空置catch块,若不得不空置,需要注释这样做的原因