Effective Java 读书笔记(八):异常

Effective Java 读书笔记(八):异常

充分发挥异常的优点,可以提高程序的可读性、可靠性和可维护性。如果使用不当,它们也会带来负面影响。

只针对异常情况才使用异常

异常应该只用于异常的情况下,它们不应该用于正常的控制流。同理,设计良好的 API 不应该强迫客户端为了正常的控制流而使用异常。如果一个类有状态相关的方法,即只有在特定的不可预知的情况下才能调用的方法,这个类也应该有个状态测试的方法,用于指示是否可以调用那个状态相关的方法。比如 Iterator 接口中状态相关的 next 方法和状态测试的 hasNext 方法。

对于可恢复的情况使用受检异常,对于编程错误使用运行时异常

如果期望调用者能够适当地恢复,就应该使用受检异常。通过抛出受检异常,强迫调用者在一个 catch 子句中处理该异常,或者将它传播出去。

通常用运行时异常来表示编程错误,多数运行时异常表示前提违例 precondition violation。

ERROR 通常用于表示资源不足、约束失败,或其他表示程序无法继续执行的情况。

避免不必要地使用受检异常

过分地使用受检异常,会使 API 用起来非常不方便。

优先使用标准的异常

好处是:
1. 使得 API 更加易于学习和使用,因为与程序员已经熟悉的习惯用法一致。
2. 少一个类,省内存。

经常被重用的异常有:

异常含义
IllegalArgumentException参数非法
IllegalStateException状态错误
NullPointerException禁止 null 的时候参数为 null
IndexOutOfBoundsException下标参数越界
ConcurrentModificationException禁止并发修改时,检测到对象的并发修改
UnsupportedOperationException对象不支持用户请求的方法

注意:一定要确保抛出异常的条件,和这些异常的文档描述一致。

抛出与抽象相对应的异常

如果方法抛出的异常与它所执行的任务没有明显的联系,这种情形会让人不知所措。当方法传递由底层抽象抛出的异常时,往往会发生这种情况。除了让人困惑之外,也让实现细节污染了更高层的 API。如果高层实现后续发生了变化,它所抛出的异常也会变化,从而潜在地破坏现有的客户端程序。

为了避免这个问题,更高层的实现应该捕获低层的异常,同时抛出按照高层抽象解释的异常。这种做法称为“异常转译”。

一种特殊的异常转译就是常见的“异常链”,高层异常提供访问方法(Throwable.getCause)来获得低层的异常。

异常不能被滥用,能避免最好,比如调用低层方法之前先做参数检查。

每个方法抛出的异常都要有文档

没有文档,其他人就很难有效地使用你的类和接口。

throws 子句中应该只有受检异常,不能包含非受检异常。但是,文档里应该既有受检异常描述,也有常见的非受检异常描述。

在细节消息中包含造成异常的信息

异常的细节信息应该包含所有“对该异常有贡献”的参数和域的值。

定制异常的时候,可以直接在构造器中输入关键参数信息,并提供访问这些信息的方法。

努力使失败保持原子性

一般而言,失败的方法调用应该使对象保持在被调用之前的状态。具有这种属性的方法被称之为具有“失败原子性”。如何做到失败原子性呢?
1. 使用不可变的对象。
2. 调整计算处理的顺序,让可能失败的部分发生在状态修改之前。
3. 修改状态之前检查参数有效性,比如 Stack 的 pop 方法会在方法开头检查栈大小。
4. 编写异常恢复代码。

如果 API 的方法不具有失败原子性,就应该在 API 文档中指明失败后对象会处于什么样的状态。

不要忽略异常

空的 catch 块会使异常达不到应有的目的。通常会在 catch 块中记录日志和监控,如果真要忽略,也有写一个注释说明为何忽略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值