Item 57 只针对异常的情况才使用异常
try-catch滥用,其负面影响是:
- 异常机制初衷是针对非正常情况,所有JVM默认不对其进行优化。比如,遍历数组每次都加入try-catch,性能可能慢2倍。
- try-catch块,会阻止JVM对其中代码的优化
- 悄悄掩盖隐藏的bug
在“有状态”情况下控制异常:
- 提供状态测试方法:如,遍历时,提供hasNext方法,用于看是否有next元素
- 提供可识别的返回值:如对象处于不适当状态时,返回null(当然,不具有通用性)
Item 58 可恢复情况->受检异常,编程错误->运行时异常
Java提供的三种可抛出结构:
- 受检的异常 checked exception
- 运行时异常 runtime exception 也称未受检异常
- 错误 error
使用受检异常还是非受捡异常,取决于:如果期望恢复,则应该使用受检异常。它们之间的区别在于:
例如在代码中写了 throw new Exception(); 和 throw new RuntimeException(); 两者都会在运行期间抛出异常!
(1)但是在编译阶段前者属于抛出一个受检查异常,要求对它进行显式的try..catch捕获处理或者向上一层方法抛出,否则在编译期间就显示错误!
(2)后者抛出是运行时异常,在编译阶段不予检查,语法上不会显示任何错误(throws处没声明不会出错,但最好声明)!
所以简单的通过throw手动抛出受检查异常和抛出运行时异常,前者要求显式处理,后者不要求作出处理。
Item 59 避免不必要的受检异常
石蕊测试:简单而有决定性的测试
抛出受检异常,强迫程序员处理异常,增加了编程负担
Item 60 优先使用标准异常
专家级程序员vs新手程序员区别之一:专家追求且通常能够实现高度的代码重用
所有错误的方法调用都可以归结为:
- 非法参数 IllegalArgumentException
- 非法状态 IllegalStateException
ConcurrentModifiedException: 如果某对象被设计为单线程或与外部同步机制配合使用,则一旦发现它被并发修改,则抛出该异常。
UnsupportedOperationException: 对象不支持所请求的操作
还有:NullPointerException IndexOutofBoundsException
Item 61 抛出与抽象对应的异常
更高层的实现,应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常 ==> 异常转译 exception translation
一种特殊的异常转译形式是:异常链 exception chaining
Item 62 抛出异常要有文档
如果许多方法抛出同一异常,则为该异常建立说明,而不是逐个方法建立说明文档。
Item 63 细节消息中包含失败信息
要把异常细节信息和用户层次的错误信息区分开
一种做法是,在异常构造器中,考虑异常描述信息的结构
Item 64 努力使失败保持原子性
失败原子性 failure atomic:失败的方法调用应该使对象保持在被调用之前的状态
办法是:
- 设计不可变对象
- 对于可变对象,
- 在使用前进行参数检查,避免异常
- 调整逻辑顺序,使得任何可能会失败的计算部分都在对象状态被改变之前发生
- 编写一段恢复代码 主要用于永久性的数据结构(比如磁盘中)
- 在对象的一份临时拷贝上执行逻辑操作
Item 65 不要忽略异常
只要将try-catch块中catch内关闭,就忽略了异常,但建议别主动关闭报警信号