《Effective Java》第9章 异常

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

Java程序设计语言提供了三种可抛出结构(throwable) ;受检的异常(checked exception)运行时异常(run-time exception)和错误(error)。

受检的异常是潜在指示

在决定使用受检的异常或是未受检的异常时,主要的原则是:如果期望调用者能够适当地恢复对于这种情况就应该使用受检的异常。通过抛出受检的异常,强迫调用者在一个catch子句中处理该异常。或者将它传播出去。因此,方法中声明要抛出的每个受检的异常,都是

对API用户的一种潜在指示:与异常相关联的条件是调用这个方法的一种可能的结果。

IOException、SQLException

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

有两种未受检的可抛出结构:运行时异常和错误。在行为上两者是等同的:它们都是不需要也不应该被捕获的可抛出结构。如果程序抛出未受检的异常或者错误,往往就属于不可恢复的情形,继续执行下去有害无益。如果程序没有捕捉到这样的可抛出结构,将会导致当前线程停止(halt),并出现适当的错误消息。

  1. Java.lang.ArithmeticException
  2. Java.lang.ClassCastException
  3. Java.lang.IllegalArgumentException
  4. Java.lang.IndexOutOfBoundsException

虽然JL5 (Java语言规范)并没有要求,但是按照惯例,错误往往被JVM保留用于表示资源不足、约束失败,或者其他使程序无法继续执行的条件。由于这己经是个几乎被普遍接受的惯例,因此最好不要再实现任何新的Error子类。因此,你实现的所有未受检的抛出结构都

应该是RuntimeException的子类(直接的或者间接的)。

API的设计者往往会忘记,异常也是个完全意义上的对象,可以在它上面定义任意的方法.

因为受检的异常往往指明了可恢复的条件,所以,对于这样的异常,提供一些辅助方法尤其重要,通过这些方法,调用者可以获得些有助于恢复的信息。例如,假设因为用户没有储存足够数量的钱,他企图在一个收费电话上进行呼叫就会失败,于是抛出受检的异常。这个异常应该提供一个访问方法,以便允许客户查询所缺的费用金额,从而可以将这个数值传递给电话用户。

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

异常转译

更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常。这种做法被称为异常转译(exception translation ),如下所示:

下面的异常转译例子取自于AbstractSequentialList类:

异常链

一种特殊的异常转译形式称为异常链(exception chaining),如果低层的异常对于调试导致高层异常的问题非常有帮助,使用异常链就很合适。低层的异常(原因)被传到高层的异常,高层的异常提供访问方法(Throwable.getCause)来获得低层的异常:

支持链

高层异常的构造器将原因传到支持链(chaining-aware)的超级构造器,因此它最终将被传给Throwable的其中一个运行异常链的构造器,例如Throwable (Throwable):

大多数标淮的异常都有支持链的构造器。对于没有支持链的异常,可以利用Throwable的initCause方法设置原因。异常链不仅让你可以通过程序(用getCause)访问原因,它还可以将原因的堆栈轨迹集成到更高层的异常中。

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

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

不可变的对象

有几种途径可以实现这种效果。最简单的办法莫过于设计一个不可变的对象。

可变对象执行前先检查

对于在可变对象上执行操作的方法,获得失败原子性最常见的办法是,在执行操作之前检查参数的有效性(见第38条)。这可以使得在对象的状态被修改之前,先抛出适当的异常。

如果取消对初始大小(size)的检杳,当这个方法企图从一个空栈中弹出元素时,它仍然会抛出异常。然而,这将会导致size域保持在不一致的状态(负数)之中,从而导致将来对该对象的任何方法调用都会失败.

编写恢复代码

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

临时拷贝

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

例如,Collections.sort在执行排序之前,首先把它的输入列表转到一个数组中,以便降低在排序的内循环中访问元素所需要的开销。这是出于性能考虑的做法,但是,它增加了一项优势:即使排序失败,它也能保证输入列表保持原样。

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

时间: 2024-10-09 10:54:31

《Effective Java》第9章 异常的相关文章

[Effective Java]第六章 枚举和注解

第六章      枚举和注解 30.      用enum代替int常量 枚举类型是指由一组固定的常量组成合法值的类型,例如一年中的季节或一副牌中的花色.在没引入枚举时,一般是声明一组int常量,每个类型成员一个常量: public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public sta

[Effective Java]第七章 方法

第七章      方法 38.      检查参数的有效性 绝大多数方法和构造器对于传递给它们的参数值都会有某些限制.例如,索引值必须是非负的,对象引用不能为null等,这些都是常见的.你应该在文档中清楚地指明所有这些限制,并且在方法体的开头处检查参数,以强制施加这些限制. 应该在方法和构造器体前进行了参数的有效性检查,并且及时向外抛出适当的异常.如果方法没有检查它的参数,就有可能发生几种情形.该方法可能在处理过程中失败,并且产生令人费解的异常,更有可能,该方法可以正常返回,但是会悄悄地计算出错

Effective Java 读书笔记之八 异常

一.只针对异常的情况才使用异常 1.类具有状态相关的方法时,可采用状态测试方法和可识别的返回值两个策略. 二.对可恢复的情况使用受检异常,对编程错误使用运行时异常 1.期望调用者能够适当恢复的情况,应该使用受检的异常. 2.用运行时异常来表明编程错误. 三.编码不必要地使用受检异常 1.使用受检异常的两个条件: a.正确地使用API并不能阻止这种异常条件的发生 b.异常发生后,使用API的程序员可以立即采取有用的动作. 四.优先使用标准的异常 1.IllegalArgumentException

[读书笔记]Effective Java 第四章

使类和成员的可访问性最小化 规则很简单:尽可能地使每个类或者成员不被外界访问.实例域(非final)决不能是公有的.当需要暴露出不可变的实例时通常会把这个实例做成不可变或者是把这个实例变成私有,同时提供该实例的备份. 在公有类中使用访问方法而非公有域 这就是常说的getter和setter方法,提供给包外访问时提供必要的方法,限制客户端的行为,以便于将来可以在内部改变表示方法. 使可变性最小化 不可变的类比可变类更加易于设计.实现和使用.它们不容易出错,且更加安全.为了使类成为不可变,要遵循下面

Effective java 第三章对于所有对象都通用的方法(一) 读书笔记

对于所有对象都通用的方法 覆盖equals时请遵守通用约定 类的每个实例本质上都是唯一的. 不关心类是否提供了逻辑相等的测试功能 超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的. 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用. throw new AssertionError() 一般覆盖Object.equals都是值类 但有一种值类不需要覆盖equals方法,即实例受控,确保每个值至多只存在一个对象的类.如枚举 覆盖equals方法,通用约定. 自

[读书笔记]Effective Java 第三章

覆盖equals方法时请遵守通用约定 这种说法的大意是要说明,Object类中定义的很多默认准则是被许多工具类或是第三方框架共同遵守的标准,一旦改动这样的标准机制,会在后续的使用中产生不稳定因素.equals方法常见用来做以下用途时,不建议对equals方法进行覆盖: 1.判断实例的唯一性 2.提供某种程度的逻辑相等 equals方法满足自反性,对称性,传递性,一致性,非空性.当需要覆盖equals方法时,需要注意以下三点: 1.覆盖equals时总要覆盖hashCode 2.不要企图让equa

Effective Java 读书笔记(一):使用静态工厂方法代替构造器

这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文<java中final与static的使用场景总结>. 什么是静态工厂方法? 可以参考书中的例子(摘自JDK1.7 java.lang.Boolean) public final class Boolean implements java.io.Serializable, Comparable<

effective java —— 终结方法守卫者

目录: effective java —— 终结方法守卫者 effective java 第2章:创建和销毁对象.第7条 : 避免使用终结方法.最后的“终结方法守卫者 (finalizer guardian)”的例子,以加深理解. 1 /** 2 * chapter 2——终结守卫者 3 * @ClassName: Parent 4 * TODO 5 * @author xingle 6 * @date 2015-3-11 下午3:49:47 7 */ 8 public class Parent

【读书笔记】《Effective Java》——创建和销毁对象

Item 1. 考虑用静态工厂方法替代构造器 获得一个类的实例时我们都会采取一个公有的构造器.Foo x = new Foo(): 同时我们应该掌握另一种方法就是静态工厂方法(static factory method). 一句话总结,静态工厂方法其实就是一个返回类的实例的静态方法. 书中给出的例子是Boolean的valueOf方法: 通过valueOf方法将boolean基本类型转换成了一个Boolean类型,返回了一个新的对象引用. 除valueOf外,像Java中的getInstance

[core java学习笔记][第十一章异常断言日志调试]

第11章 异常,断言,日志,调试 处理错误 捕获异常 使用异常机制的技巧 使用断言 日志 測试技巧 GUI程序排错技巧 使用调试器 11.1 处理错误 11.1.1异常分类 都继承自Throwable类 分成Error和Exception Error类 描写叙述了Java运行时系统的内部错误和资源耗尽错误. 应用程序不应该抛出此种类型的错误.假设出现了这样的内部错误.除了通告给用户,并尽力使程序安全地终止外,再也无能为力 Exception层次结构:最需关注的 RuntimeException