Java异常(二) 《Effective Java》中关于异常处理的几条建议(一)

2014-11-24 02:40:52 · 作者: · 浏览: 7
第1条: 只针对不正常的情况才使用异常
建议:异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流。
通过比较下面的两份代码进行说明。
代码1
复制代码
try {
int i=0;
while (true) {
arr[i]=0;
i++;
}
} catch (IndexOutOfBoundsException e) {
}
复制代码
代码2
for (int i=0; i
arr[i]=0;
}
  两份代码的作用都是遍历arr数组,并设置数组中每一个元素的值为0。代码1的是通过异常来终止,看起来非常难懂,代码2是通过数组边界来终止。我们应该避免使用代码1这种方式,主要原因有三点:
异常机制的设计初衷是用于不正常的情况,所以很少会会JVM实现试图对它们的性能进行优化。所以,创建、抛出和捕获异常的开销是很昂贵的。
把代码放在try-catch中返回阻止了JVM实现本来可能要执行的某些特定的优化。
对数组进行遍历的标准模式并不会导致冗余的检查,有些现代的JVM实现会将它们优化掉。
实际上,基于异常的模式比标准模式要慢得多。测试代码如下:
复制代码
public class Advice1 {
private static int[] arr = new int[]{1,2,3,4,5};
private static int SIZE = 10000;
public static void main(String[] args) {
long s1 = System.currentTimeMillis();
for (int i=0; i
endByRange(arr);
long e1 = System.currentTimeMillis();
System.out.println("endByRange time:"+(e1-s1)+"ms" );
long s2 = System.currentTimeMillis();
for (int i=0; i
endByException(arr);
long e2 = System.currentTimeMillis();
System.out.println("endByException time:"+(e2-s2)+"ms" );
}
// 遍历arr数组: 通过异常的方式
private static void endByException(int[] arr) {
try {
int i=0;
while (true) {
arr[i]=0;
i++;
//System.out.println("endByRange: arr["+i+"]="+arr[i]);
}
} catch (IndexOutOfBoundsException e) {
}
}
// 遍历arr数组: 通过边界的方式
private static void endByRange(int[] arr) {
for (int i=0; i
arr[i]=0;
//System.out.println("endByException: arr["+i+"]="+arr[i]);
}
}
}
复制代码
运行结果:
endByRange time:8ms
endByException time:16ms
结果说明:通过异常遍历的速度比普通方式遍历数组慢很多!
第2条: 对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
运行时异常 -- RuntimeException类及其子类都被称为运行时异常。
被检查的异常 -- Exception类本身,以及Exception的子类中除了"运行时异常"之外的其它子类都属于被检查异常。
  它们的区别是: Java编译器会对"被检查的异常"进行检查,而对"运行时异常"不会检查。也就是说,对于被检查的异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。而对于运行时异常,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。当然,虽说Java编译器不会检查运行时异常,但是,我们同样可以通过throws对该异常进行说明,或通过try-catch进行捕获。
  ArithmeticException(例如,除数为0),IndexOutOfBoundsException(例如,数组越界)等都属于运行时异常。对于这种异常,我们应该通过修改代码进行避免它的产生。而对于被检查的异常,则可以通过处理让程序恢复运行。例如,假设因为一个用户没有存储足够数量的前,所以他在企图在一个收费电话上进行呼叫就会失败;于是就将一个被检查异常抛出。
第3条: 避免不必要的使用被检查的异常
  "被检查的异常"是Java语言的一个很好的特性。与返回代码不同,"被检查的异常"会强迫程序员处理例外的条件,大大提高了程序的可靠性。
  但是,过分使用被检查异常会使API用起来非常不方便。如果一个方法抛出一个或多个被检查的异常,那么调用该方法的代码则必须在一个或多个catch语句块中处理这些异常,或者必须通过throws声明抛出这些异常。 无论是通过catch处理,还是通过throws声明抛出,都给程序员添加了不可忽略的负担。
  适用于"被检查的异常"必须同时满足两个条件:第一,即使正确使用API并不能阻止异常条件的发生。第二,一旦产生了异常,使用API的程序员可以采取有用的动作对程序进行处理。
第4条: 尽量使用标准的异常
代码重用是值得提倡的,这是一条通用规则,异常也不例外。重用现有的异常有几个好处:
第一,它使得你的API更加易于学习和使用,因为它与程序员原来已经熟悉的习惯用法是一致的。
第二,对于用到这些API的程序而言,它们的可读性更好,因为它们不会充斥着程序员不熟悉的异常。
第三,异常类越少,意味着内存占用越小,并且转载这些类的时间开销也越小。
Java标准异常中有几个是经常被使用的异常。如下表格:
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ 异常 ┃ 使用场合 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━