【effective java】57~65.异常

第57条:只针对异常的条件才使用异常

异常是为了在异常情况下使用而设计的。不要将他们用于普通的控制流,也不要编写迫使他们这么做的API。

如何正确的使用异常:https://www.aliyun.com/jiaocheng/550636.html

第58条:对可恢复的条件使用受检异常,对编程错误使用运行时异常

java提供了三种可以抛出的结构:

1. 受检异常(checked exception)

2. 运行时异常(runtime exception)

3. 错误(error)

运行时异常和错误都是不需要也不应该被捕获的可抛出结构。如果程序抛出运行时异常或者错误,说明出现了不可恢复的情形,继续执行下去有害无益。如果没有捕捉到这样的结构,将会导致当前线程停止,并出现适当的错误消息。

扫描二维码关注公众号,回复: 2620730 查看本文章

使用原则:

1. 如果期望调用者能够适当地恢复,对于这种情况就使用受检的异常。

2. 用运行时异常来表明编程错误。

3. 如果不清楚是否有可能恢复,则使用未受检异常。

第59条:避免不必要地使用受检的异常

受检异常强迫程序员处理异常条件。

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

受检异常的使用场景:

如果正确地使用API并不能阻止这种异常条件的产生;

并且一旦产生异常,使用API的程序员可以立即采取有效的动作。

如果这两个条件都成立,这种负担就是正当的。否则就适用于未受检的异常。

第60条:优先使用标准的异常

为什么?

第一,使用标准异常和程序员原来已经熟悉的API是一致的。

第二,可读性好。不会出现程序员不熟悉的异常。

第三,使用异常类越少,意味着内存占用就越小,并且装载这些类的时间开销也越小。

异常

使用场合

IllegalArgumentException

参数值不正确

IllegalStateException

对于方法调用而言,对象状态不合适

NullPointerException

在禁止使用null的情况下参数值为null

IndexOutOfBoundsException

下标参数值越界

ConcurrentModificationException

在禁止并发修改的情况下,检测到对象的并发修改

UnsupportedOperationException

对象不支持用户请求的方法

第61条:抛出与抽象相对应的异常

想想这样一种情况:方法B抛出了一个受检的异常 ,那么方法A在内部调用方法B时,面对方法B抛出的受检异常,可以选择继续抛出向上传播这个异常,也可以捕获这个异常进行处理。究竟是向上传播抛出,还是捕获处理呢???

1)异常转译:更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常

使用异常链来让高层抽象包含低层的异常,便于以后排除问题。

2)可以在给低层传递参数之前,检查更高层方法的参数的有效性,从而避免低层方法抛出异常。

3)如果无法避免低层异常,可以让更高层来悄悄地绕开这些异常,从而将高层方法的调用者与低层的问题隔离开来。使用适当地记录机制来将异常记录下来。

4)除非低层方法碰巧可以保证它抛出的所有异常对高层也合适才可以将异常从低层传播到高层。

第62条:每个方法抛出的所有异常都要有文档

要为你编写的每个方法所能抛出的每个异常建立文档。对于未受检和受检的异常,以及对于抽象的和具体的方法也都一样。要为每个受检异常提供单独的throws子句,不要为未受检的异常提供throws子句。如果没有为可以抛出的异常建立文档,其他人就很难或者根本不可能有效地使用你的类和接口。

第63条:在细节消息中包含能捕获失败的信息

这一条是写给那些自己写Exception的程序员的。

为了在异常发生后捕捉失败原因,一个异常的字符串表示应该包括所有“对该异常有贡献”的参数和域的值。也就是说在异常所能“携带”的信息中,尽量多的加入对调试人员有用的信息。

一个例子是 IndexOutOfBoundsException ,在这个下标越界的异常中,提示出了,最小小标、最大下标和当前下标的值,这样调试人员就可以很清楚的知道到底是上溢出了,还是下溢出了,溢出了多少。

这一条对于自己扩展Exception类来说,非常有意义。扩展时不仅仅是继承已有的异常就行了,纵然可以通过异常的名称来获得错误原因,但这往往是不够的,失败发生当时的信息必须通过异常的字符串表示传达出来。

第64条:努力使失败保持原子性

一般而言,失败的方法调用应该使对象保持在被调用之前的状态。具有这种属性的方法被成为具有失败原子性(failure atomic)。

失败原子性实现方法:

1)第1种:设计一个不可变的对象(见第15条)

2)第2种:对于在可变对象上执行操作的方法,获得失败原子性最常见的方法是,在执行操作之前检查参数的有效性

3)第3种:一种类似的获得失败原子性的办法是,调整计算处理过程的顺序,使得任何可能会失败的计算部分都在对象状态被修改之前发生。

4)第4种:获得失败原子性的办法远远没有那么常用,用法是编写一段恢复代码(recovery code),由它来拦截操作过程中发生的失败,以及使对象会滚到操作开始之前的状态上。这种办法主要用于永久性的(基于磁盘的(disk-based)数据结构)。

5)第5种:获得失败原子性的办法是,在对象的一份临时拷贝上执行操作,当操作完成之后再用临时拷贝中的结果代替对象的内容。

错误(相对于异常)通常是不可恢复的,当方法抛出错误时,它们不需要努力保持失败原子性。

一般而言,作为方法规范的一部分,产生的任何异常都应该让对象保持在该方法调用之前的状态。如果违反这条规则,API文档就应该清楚地指明对象将会处于什么样的状态。

第65条:不要忽略异常

不管异常代表了可预见的异常条件,还是编程错误,用空的catch块忽略他。将会导致程序在遇到错误的情况下悄然的执行下去。然后,有可能在将来的某个点上,当程序不能再容忍与错误源明显相关的问题时,他就会失败。正确的处理异常能够彻底挽回始变。只要将异常传播给外界,至少会导致程序迅速的失败,从而保留了有助于调试该失败条件的信息。

猜你喜欢

转载自blog.csdn.net/CharJay_Lin/article/details/81428887