一、优先使用标准异常(第60条)
1. 使用标准异常的好处
- 它使API更加易于学习和使用,因为它与程序员已经熟悉的习惯用法一致。
- 对于用到这些API的程序而言,它们的可读性会更高,因为它们不会出现很多程序员不熟悉的异常。
- 异常类越少,意味着内存印迹就越小,装载这些类的时间开销也越少。
2. 常用标准异常
异常名称 | 使用场合 | 备注 |
---|---|---|
IllegalArgumentException | 非NULL的参数值不合法 | 如果一个参数代表某个”动作的重复次数”,而调用者传入一个负数 |
IllegalStateException | 对于方法调用而言对象状态不合适 | 如果因为接收对象的状态而使调用非法就会抛出该异常 |
NullPointerException | 在禁止使用null的情况下参数值为null | - |
IndexOutOfBoundsException | 下标参数值越界 | - |
二、抛出与对象相对应的异常(第61条)
如果方法抛出的异常与它所执行的任务没有明显的联系(与API中定义的异常不一致),这种情况会使人不知所措,当方法传递有底层的异常时往往会发生这种情况。
1. 更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常(API中定义的异常),这种做法被称为异常转义。
2. 一种特殊的异常转译形式被称为异常链,如果低层的异常对于调试导致高层异常的问题非常有帮助,使用异常链就很合适,低层的异常被传至高层异常,高层的异常提供访问方法来获取低层的异常。
try{
//code....
}catch (LowerLevelException e){
throw new HigherLevelException(e);
}
3、 尽管异常转译与不加选择的从低层传递异常的做法相比有所改进,但是它也不能被滥用,如果有可能,处理来自低层异常的最好做法是在调用低层方法之前确保它们会成功执行,从而避免他们抛出异常。有时候,可以在给低层传递参数之前检查更高层方法参数的有效性,从而避免低层方法抛出异常。
三、每个方法抛出的异常都要有文档(第62条)
1. 始终要单独地的声明受检查的异常,并且利用JavaDoc的@throws标记,准确的记录下抛出的每个异常条件。
- 如果一个方法可能抛出多个异常类,则不要使用”快捷方式”声明它会抛出这些异常类的某个超类(如:永远不要声明一个方法
throws Exception
)。
2. 使用JavaDoc的@throws标签记录下一个方法可能抛出的每个未受检查异常,但是不要使用throws关键字将未受检查异常包含在方法声明中。
- 虽然Java语言本身并不要求程序员为一个方法声明它可能会抛出的未受检查异常,但是,如同受检查异常一样,仔细的为它们建立文档是非常明智的。
- 对于接口中的方法,在文档中记录下它可能抛出的未受检查异常显得尤为重要,这份文档成了该接口的通用约定的一部分,它指定了该接口的多个实现必须遵循的公共行为。
四、在细节消息中包含能捕获失败的信息(第63条)
为了确保在异常的细节消息中包含足够的能捕获失败的信息,一种办法是在异常的构造器而不是字符串细节消息中引入这些信息,然后有了这些信息,只要把它们放到消息描述中,就可以自动产生细节消息.
例如:IndexOutOfBoundsException 异常的细节信息应该包含下界、上界以及没有落在界内下标值,该细节消息提供了许多关于失败的信息。
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index){
super("Lower bound :"+lowerBound + ",Uper bound:"+upperBound+", Index :"+index);
this.lowerBound = lowerBound ;
this.upperBound = upperBound ;
this.index = index ;
}
五、努力使失败保持原子性(第64条)
1. 失败的方法调用应该使对象保持在被调用之前的状态,具有这种属性的方法被称为具有失败原子性。
2. 使方法具有失败原子性的几种途径:
- 设计一个不可变的对象,如果对象是不可变的,失败原子性就是显然的。如果一个操作失败了,它可能会阻止创建新的对象,但是永远也不会使已有的对象保持在不一致的状态之中,因为每个对象被创建之后它就处于一致的状态之中,以后也不会再发生变化。
- 对于在可变对象上执行操作的方法,获得失败原子性最常见的办法是在执行操作之前检查参数的有效性这可以使得在对象的状态被修改之前先抛出适当的异常。
- 调整计算过程处理的顺序,使得任何可能会失败的计算部分都在对象状态被修改之前发生。
- 第四种办法远远没有那么常用,做法是编写一段恢复代码,由他拦截操作过程中发生的失败,以及是对象回滚到最初状态。
- 最后一种获得对象原子性的办法是,在对象的一份临时拷贝上执行操作,当操作完之后再用临时拷贝中的结果代替对象的内容。
不能忽略异常
不管异常代表了可预见的异常条件还是编程错误(检查异常及非检查异常),用空的catch块忽略它,将会导致程序在遇到错误的情况下悄悄地执行下去。