异常应该只被用于异常的情况下,永远不应该用于正常的控制流
设计良好的API也不应该强迫他的客户端使用异常控制流
// 可用 for(Iteratori = collection.iterator(); i.hasNext(); ){ Foo f = i.next(); } // 不用 try{ Iterator i = collection.iterator(); while(TRUE){ Foo f = i.next(); } }catch( NoSuchElementException e){ ... }
第70条 对可恢复的情况使用受检异常,编程错误使用运行时异常
受检异常:checked exception
未受检异常:
运行时异常:run-time exception
错误:error
可以恢复的对象,抛出受检异常; 程序错误,抛出运行时异常; 不确定是否可以恢复,抛出非受检异常。可以在受检异常上提供方法,便于协助恢复
第71条 避免不必要的使用受检异常
抛出受检异常的方法不能用于Stream流
在谨慎使用的前提下,受检异常可以提高程序的可读性;但过度使用,API会非常难读;
如果调用者无法恢复失败,应该抛出非受检异常;如果可以恢复,且想要迫使调用者处理异常的条件,应该返回一个Optional值; 当且仅当失败时无法提供足够的信息,抛出受检异常
第72条 优先使用标准的异常
一些可重用异常:
| 异常 | 使用场合 |
|---|---|
| IllegalArgumentException | 非null的参数值不正确 |
| IllegalStateException | 不适合方法调用的对象状态 |
| NullPointerException | 在禁止使用null值的情况下参数值为NULL |
| IndexOutOfBoundsException | 下标参数值越界 |
| ConcurrentModificationException | 禁止并发修改的情况下,检测到对象的并发修改 |
| UnsupportedOperationException | 对象不支持用户的使用方法 |
异常是可序列化的,因此没有非常正当的理由,不要自己编写异常类
第73条 抛出与抽象对应的异常
异常转译:更高层的异常应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常
public E get(int index){ ListIteratori = listInterator(index); try{ return i.next(); }catch (NoSuchElementExcetion e){ throw new IndexOutOfBoundsException(...) } } 异常链:特殊的异常转译,将产生异常的原因传到高层,高层使用getCause方法获取
异常链可以通过程序(getCause)访问原因,还可以将原因的堆栈轨迹集中到更高层的异常中
一般不能阻止或者处理低层的异常,通常做法是用异常转译
第74条 每个方法抛出的所有异常都需要建立文档
为你编写的每个方法所能抛出额每个异常(受检、非受检)建立文档,文档注释采用javadoc的@throws关键字
第75条 在细节消息中包含失败-捕获信息
程序因未被捕获的异常失败时,系统会自动打印异常的堆栈轨迹,其中包含异常的字符串表示法(toString),为了捕获失败,这些细节信息应该包括“对该异常有贡献的所有参数和域的值
注意不要在细节消息中包含密码、密钥及类似信息
第76条 努力使失败保持原子性
失败原子性: 失败的方法调用使对象保持在被调用之前的状态
几种途径:
- 设计一个不可变对象(只针对不可变对象,以下为可变对象)
- 执行操作前,检查参数的有效性
- 调整计算处理的过程,使可能失败的计算部分都在对象状态改变之前发生
- 在对象的一份临时拷贝上执行操作,完成后用临时拷贝的结果代替对象的内容
- 编写一段回复代码(类似回滚)-- 不推荐
方法规范:方法产生的任何异常都应该让对象保持在调用该方法之前的状态
第77条 不要忽略异常
空的catch块通常会使异常达不到应有的目的,也是非常危险的
如果选择忽略异常,catch中应该包含一条注释,说明理由,且变量名改为 ignore
Futuref = exec.submit(plannarMap::chromaticNumber); int numColors = 4; try{ numColors = f.get(1L, TimeUnit.SECONDS); }catch(TimeoutException | ExecutionException ignored){ // ... not required }



