以下为我在《Effective Java》中留下的读书笔记,对一些重要的知识点提取出了摘要.
57、只针对异常的情况才使用异常
使用标准模式,因为现代的JVM实现可能会将它们优化.
异常只用于异常的情况下,它们永远都不应该用于正常的控制流.
设计良好的API不应该强迫它的客户端为了正常的控制流而使用异常.
“状态测试方法” 与 "可识别的返回值" 两种做法的选择.
58、对可恢复的情况使用受检异常,对编程错误使用运行时异常
三种可抛出结构:受检异常、运行时异常和错误
如果期望调用者能够适当地恢复,对于这种情况就应该使用受检异常.
用运行时异常来表明编程错误 (RuntimeException)
异常也是完全意义上的对象,可以在上面定义任意的方法.这些方法的主要用途是为捕获异常的代码而提供额外的信息,特别是关于引发这个异常条件的信息.例如,假设因为用户没有储存足够数量的钱,他企图在一个收费电话上进行呼叫就会失败,于是抛出受检的异常.这个异常应该提供一个访问方法,以便允许客户查询所缺的费用金额.
59、避免不必要地使用受检的异常
受检异常会对程序员造成负担.如果正确地使用API并不能阻止这种异常条件的产生,并且一旦产生异常,使用API的程序员可以立即采取有用的动作,这种负担就被认为是正当的.除非这两个条件都成立,否则更适合使用未受检的异常.
添加 状态测试方法 以此把受检的异常变成未受检的异常.
60、优先使用标准的异常
所有错误的方法调用都可以被归结为非法参数或者非法状态
IllegalArgumentException 非法的参数异常
IllegalStateException 例如,在某个对象被正确地初始化之前,调用者就企图使用这个对象.
NullPointerException
IndexOutOfBoundsException
ConcurrentModificationException 如果一个对象被设计为专用于单线程或者与外部同步机制配合使用,一旦发现它正在或已经被并发地修改,就可以抛出此异常.
UnsupportedOperationException 如果接口的具体实现没有实现该接口所定义的一个或多个可选操作,它就可以使用这个异常
61、抛出与抽象相对应的异常
异常转译 更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常.
异常链 大多数标准异常都有支持链的构造器,对于没有支持链的异常,可以利用Throwable的initCause方法设置原因.
如果无法避免低层异常,次选方案是将高层方法的调用者与低层的问题隔离开来.可以用某种适当的记录机制(如java.util.logging)将异常记录下来.
62、每个方法抛出的异常都要有文档
方法声明受检异常,不声明未受检异常.
@throws 记录下所有受检异常和未受检异常.
63、在细节消息中包含能捕获失败的信息
为了捕获失败,异常的细节信息应该包含所有"对该异常有贡献"的参数和域的值.
异常的细节信息不应该与“用户层次的错误消息”混为一谈.前者注重内容,后者注重可理解性.
64、努力使失败保持原子性
保持失败原子性的方法: 设计一个不可变的对象
在执行操作之前检查参数的有效性
编写一段恢复代码(回滚)
在对象的一份临时拷贝上执行操作
65、不要忽略异常
不应该有空的catch块. 至少,catch块也应该包含一条说明,解释为什么可以忽略这个异常.