用异常来处理错误----第三节 捕获和处理异常

    这节说明怎样使用异常处理器的三个组成部分-try,catch和finally块来编写异常处理器。最后举一个例子,并且分析在不同的情况下发生了什么。
    下面的例子定义和实现了一个叫做ListOfNumbers的类。在类的构造器中,ListOfNumbers创建了一个Vector,它包含了从0到9 的十个连续的整数。ListOfNumbers类也定义了一个叫writeList的方法,这个方法把这个数字列表写入一个叫做OutFile.txt的文本文件中。这个例子使用了在java.io中定义的输出类。
// 注意: 这样的设计,这个类不会被编译
import java.io.*;
import java.util.Vector;

public class ListOfNumbers {
    private Vector victor;
    private static final int SIZE = 10;
    public ListOfNumbers () {
        victor = new Vector(SIZE);
        for (int i = 0; i < SIZE; i++) {
            victor.addElement(new Integer(i));
        }
    }

    public void writeList() {
        PrintWriter out = new PrintWriter(
                            new FileWriter("OutFile.txt"));
        for (int i = 0; i < SIZE; i++) {
            out.println("Value at: " + i + " = " +
                            victor.elementAt(i));
        }
        out.close();
    }
}
  这个例子中的第一行黑体字部分代码调用了一个构造器,这个构造器初始化一个文件输出流。如果这个文件不能被打开,这个构造器会抛出一个 IOException异常。第二行黑体字部分代码调用一个Vector类的elementAt方法,如果它的参数值太小(小于零)或太大(大于 Vector中当前所包含的元素数),那么它会抛出一个ArrayIndexOutOfBoundsException异常。
如果试图编译ListOfNumbers类,编译会打印一个有关被FileWrite构造器所抛出的异常的错误消息。这是因为构造所抛出的 IOException异常是一个编译检查性异常,被elementAt方法抛出的ArrayIndexOutOfBoundsException异常是一个运行时异常,而Java编程语言只要求程序处理编译检查性异常,所以你只能获取一个错误消息。
  现在随着对ListOfNumbers类的熟悉,并且知道异常是在程序中的什么地方抛出的,那么你就可以准备把异常处理器编写到catch块来处理那些异常。
怎样抛出异常
  在你能够捕捉一个异常之前,在程序中的某个地方必须有抛出这个异常的代码在在。任何代码都可以抛出异常:它们可以你自己的代码,也可以是来自于别人所写的包中的代码(例如与Java平台一起提供的程序包),或者是Java运行时环境。不管抛出什么样的异常,都要使用throw语句把异常抛出。
  你可能已经注意到,Java平台提供了各种的异常类。所有的这些类都是Throwable类的子类,并且它们都允许程序来区分在程序执行期间所发生的各种类型的异常。
  你也可以创建自己的异常类来描述你编写的类中所发生的问题。实际上,如果你是一个程序包的开发人员,你可能必须创建你自己的异常类的集合,以便于让你的用户来区分在你的程序包中发生的错误是来自己于Java平台还是其它的包。
  你也可以创建异常链,异常链在Java Standard Edition 1.4中被引入。更多的信息,请看"异常链"这一节。

"throw"语句
   所有的方法都使用"throw"语句来抛出一个异常。Throw语句需要一个单独throwable对象,这个对象是任意Throwable类的子类。如下类所示:
throw someThrowableObject;
   让我们在程序的上下文中来看一下throw语句。下面的pop方法把来自于一个公共堆栈中的一个执行类给删除。这个方法从堆栈上面的元素,并且返回被删除的对象。
public Object pop() throws EmptyStackException {
    Object obj;
    if (size == 0) {
        throw new EmptyStackException();
    }
    obj = objectAt(SIZE - 1);
    setObjectAt(SIZE - 1, null);
    size--;
    return obj;
}
   pop方法检查堆栈上是否有元素。如果堆栈是空的(也就是说它的尺寸等于0),pop方法就会实例化一个新的EmptyStackException对象(它是java.util中的一个成员),并且抛出它。在这章的后面一节会解释怎样创建自己的异常类。对于现在,你所需要记住的是你只能抛出继承于 java.lang.Throwable类的对象。
注意,pop方法的声明中包含了一个throws子句。EmptyStackException是一个检查性异常,并且pop方法没有捕捉这个异常。因此,这个方法必须使用throws子名来声明它所抛出的异常的类型。

Throwable 类和它的子类

  继承Throwable类的对象包括直接子类(直接继承于Throwable类的对象)和间接子类(继承于Throwable类的子类的对象)。下图说明了Throwable类的层次关系和最主要的一些子类。象你看到的一样,Throws有两个直接的子类:Error类和Exception类。

 

 

Error类
   当在Java虚拟机中发生动态连接失败或其它的定位失败的时候,Java虚拟机抛出一个Error对象。典型的简易程序不捕获或抛出Errors对象。
Exception类
   大多数程序都抛出或捕获衍生于Exception类的对象。一个异常表明发生了一个问题,但它不是严重的系统问题。你编定的大多数程序将会抛出或捕获Exceptions对象(而不是Errors对象)。
   在Java平台中Exception类有许多已经定义了的子类。这些子类说明所发生的异常的各种类型。例如,IllegalAccessException异常类说明了不能找到一个特殊的方法;NegativeArraySizeException异常类说明程序试图创建一个带有负尺寸的数组。
   有一个特殊的Exception子类:RuntimeException。这个子类是在程序运行期间在Java虚拟机内部所发生的异常。例如 NullPointerException类就是一个运行时异常类,在一个方法试图通过一个null引用来访问一个对象的成员时会发生这个异常。在 Unchecked Exceptions---The Controversy这一节中,我们会讨论为什么典型的程序不应该抛出运行时异常或RuntimException类的子类异常对象。

有争议性的未被检查的异常
   因为Java编程语言不要求方法一定要捕获或列出运行异常或错误,所以程序员就可能被误导,编写只抛出运行时异常或者使所有的异常子类都继承于 RuntimException的代码,这两种快捷方式允许程序员编写不用为编译错误而操心的代码,并且也不费神去指定或捕获任何异常。尽管这种方法看上去对程序员很方便,但它回避了捕获或指定必要的东西的意图,并且可能使使用你的类的程序员发生错误。
   为什么设计者决定强制一个方法指定所有的在它的范围内可能被抛出的未检查异常呢?被一个方法抛出的任何异常都是方法公共编程接口的一部分。方法的调用者必须知道有关这个方法所抛出的异常,以便他们能够针对这些异常决定做什么。这些异常是编写方法的接口差不多,有它们的参数和返回值。
   接下来你的问题可能是:如果它是这么好的说明了一个方法的API,包括它能抛出的异常,那么为什么不也指定运行时异常呢?运行时异常描述的问题是一个设计问题的结果,并且,API的客户代码不能期望从出错的地方来恢复程序执行或用一些方法来处理它们。这些问题包括算法异常(例如被零除),指针异常(例如通过一个空的引用来访问一个对象),以及索引异常(例如试图通过一个越界的索引来访问一个数组)。运行时异常可能在程序的任何地方发生,并且在一个典型的程序中可能有很多,因此,在每个方法中不得不添加运行异常来降低程序的透明度,这样,编译器不要求你指定或捕获运行时异常(尽管你可以)。
   抛出运行时异常(RuntimeException)的一个公共案例就是在用户调用了一个错误的方法的时候。例如,一个方法检查它的参数是否有效,如果一个参数是空(null),那么这个方法就可能抛出一个NullPointerException异常,这是一个不检查异常。
   一般来说,不抛出运行时异常(RuntimeException)或不创建一个运行时异常(RuntimeException)的子类的原因是:人你不想为指定你的方法所能抛出的异常而操心。
   一个使用异常的方针是:如果客户能够被期望从一个异常中得到恢复,那么就要使用检查性异常。如果一个客户对于从异常中恢复的程序不能做任何事,那么就可以使用不检查性异常。

posted @ 2009-01-14 19:35  andriod2012  阅读(181)  评论(0编辑  收藏  举报