现在的位置: 首页 > 综合 > 正文

java异常分析

2013年10月15日 ⁄ 综合 ⁄ 共 7676字 ⁄ 字号 评论关闭

10.3  抛出异常

在前面的小节中已经提到过异常抛出的概念,如果在try块中抛出的异常没有能够捕获它的catch块,或者说,捕获这个异常但不想立即处理,则Java将立即退出这个方法,并将其返回到上一级处理,如此可以不断地递归向上直到最外一级。同时,在方法的声明中要指定方法中可能产生的异常,使这个方法的调用者准备好处理这种异常的代码,这样,这种类型的异常在此方法的调用者中得到了处理。调用者可能自己处理这种异常,也可能将这个异常放给它的调用者。异常就这样逐级上溯,直到找到处理它的代码为止。如果没有任何代码来捕获并处理这个异常,Java将结束这个程序的执行。

所以,当不在方法中直接捕获被检查的异常时,必须用throw语句将异常抛给上层的调用者。也就是说,通过throw语句可以明确地抛出一个异常,同时在方法中用throws声明此方法将抛出某类型的异常。

本节讲述异常抛出语句、异常声明、Throwable类以及它的子类。

10.3.1  异常抛出语句

当不在方法中直接捕获被检查的异常时,必须指定它可以抛出的所有被检查的异常。抛出异常所做的两部分工作是:在它的声明中使用throws语句指定它可以抛出的异常,同时使用throw语句抛出异常。

在方法中声明可能出现的异常,这样做的目的是让方法可以告诉编译器它可能会产生哪些异常,从而要求它的使用者必须考虑对这些异常的处理,这样就使异常及时得到处理,减少了程序崩溃的几率。

Java可能会抛出异常的情况包括:调用的方法抛出了异常、检测到了错误并使用throw语句抛出异常、程序代码有错误,从而导致异常,比如数组越界错误、Java运行时刻系统产生内部错误。当前两种异常发生时,应该告诉使用这个方法的人,此方法强迫Java抛出异常。因为任何抛出异常的方法都是导致程序死亡的陷阱,如果没有任何代码来处理方法抛出的异常,就会导致程序结束。下面讲解throw语句和throws声明。

1.throw语句

在使用throw语句时,需要一个单独Throwable对象,这个对象是任意Throwable类的子类,其使用格式如下。

throw someThrowableObject;

程序会在运行到throw语句后立即终止,它后面的语句都不执行,然后在包含它的所有try块中从里到外寻找含有与其匹配的catch。

现在,在程序的上下文中来看一下throw语句。下面的pop方法删除一个公共堆栈中的一个执行类。这个方法从堆栈上面的元素开始执行,并且返回被删除的对象,如代码10-11所示。

【代码10-11】  throw语句示例

public Object pop() throws EmptyStackException {

     Object obj;

     if (size == 0) {

         //throw语句

         throw new EmptyStackException();

     }

     obj = objectAt(SIZE - 1);

     setObjectAt(SIZE - 1, null);

     size--;

     return obj;

}

在代码10-11中,pop方法检查堆栈上是否有元素。如果堆栈是空的(也就是说它的尺寸等于0),pop方法就会实例化一个新的EmptyStackException对象(它是java.util中的一个成员),并且抛出它。在10.4节中,会解释怎样创建自己的异常类。在这里只能抛出继承于java.lang.Throwable类的对象。

2.throws声明

在代码10-11中,pop方法的声明中包含了一个throws子句。EmptyStackException是一个检查性异常,并且pop方法没有捕捉这个异常。因此,这个方法必须使用throws语句来声明它所抛出的异常的类型。

throws子句在方法的声明中,用于指定方法中可能产生的异常。

throws子句的格式如代码10-12所示。

【代码10-12】  throws声明示例1

type method_name(arg_list) throws WhcaException{

    ……

}

举个例子,如代码10-13所示。

【代码10-13】  throws声明示例2

class Animation{

     public Image loadImage(String s) throws EOFException {

         ……

         ……

     }

}

在代码10-13中声明loadImage()方法时,在后面加上了throws EOFException异常抛出声明,表明在这个方法中可能会抛出EOFException异常。

如果一个方法可能抛出的异常不止一个,可以在方法的throws子句中声明多个异常,这些异常使用逗号“,”隔开。代码10-13则变为如代码10-14所示。

【代码10-14】  throws声明示例3

class Animation{

    public Image loadImage(String s)

            throws EOFException, MalformURLException{

        ……

        ……

    }

}

并不是所有可能发生的异常都要在方法的声明中指定,从Error类中派生出的异常和从RuntimeException类中派生的异常就不用在方法声明中指定。这两类异常属于不检查异常(unchecked exception)。Java语言中的另一类异常是检查异常(checked exception)。检查异常是那些在程序中应该处理的异常,而不检查异常则是那些无法干预的异常(如Error类异常)或者完全在控制之下可以避免的异常(比如数组越界错误)。而方法的声明中必须指明所有可能发生的检查异常。关于检查异常和不检查异常以及Error类、Exception类等将在10.3.2小节中详细讨论。

方法实际抛出的异常可能是throws子句中指定的异常类或其子类的实例。比如在方法的声明中指明方法可能产生IOException,但是实际上可能抛出的异常或许是EOFException类的实例,这些异常类都是IOException的子类。

注意:当子类的方法覆盖了超类的方法时,子类方法的throws子句中声明的异常不能多于超类方法中声明的异常,否则会产生编译错误。因此,如果超类的方法没有throws子句,那么子类中覆盖它的方法也不能使用throws子句指明异常。对于接口,情况相同。

下面来看一个例子,如代码10-15所示。

【代码10-15】  throws声明示例4

 import java.io.IOException;

public class ExceptionUse{

   

     public static void main(String[] args){

         try{

              System.out.println("");

              System.out.println("please enter some characters:");

              String s = getInput();

              System.out.println("");

              System.out.println("The characters you entered is:");

              System.out.println(s);

         }

       

         catch(IOException e){

              System.out.println("sorry, but"+e.getMessage());

        }

    }

    //throws指明可能会抛出IOException异常

    static String getInput() throws IOException{

       

         char[] buffer = new char[20];

         int counter = 0;

         boolean flag = true;

       

         while(flag){

              buffer[counter] = (char)System.in.read();

              if (buffer[counter]=='/n')

                   flag = false;

                  counter++;

              if (counter>=20){

                   IOException myException = new IOException("buffer is full");

                   throw myException;

              }

         }

         return new String(buffer);

    }

}

程序的输出结果如图10-12所示。

 

http://book.csdn.net/BookFiles/319/img/image017.jpg

图10-12  throws声明示例4运行结果

在代码10-15中,声明了一个方法getInput(),用来获取从键盘输入的字符,并以字符串对象的形式返回。程序里设定从键盘输入的字符数不能多于20个,否则就抛出一个IOException异常。由于在方法getInput()中,可能会产生IOException异常,同时,方法本身并没有提供捕获语句,所以在方法的声明中必须用throws指明这个异常,如:

  static String getInput() throws IOException

在方法getInput()中,可能产生异常的地方有两处,一是Java的InputStream类的read()方法。InputStream类的read()方法的原型如下。

  Public abstract int read() throws IOException

在方法getInput()中并没有捕获和处理这个异常,如:

  buffer[counter] = (char)System.in.read();

另一个可能产生异常的地方是方法中的while循环,在这个循环中,如果输入的字符数大于20,就抛出一个IOException异常,如:

                counter++;

            if (counter>=20){

                IOException myException = new IOException("buffer is full");

                throw myException;

            }

在方法main中,将捕获这两个异常,并打印出异常的信息。如果没有异常发生,就打印出得到的输入字符串,如:

        catch(IOException e){

            System.out.println("sorry, but"+e.getMessage());

        }

3.异常抛出

由此可见,抛出异常需要经过以下3个步骤。

(1)确定异常类。

(2)创建异常类的实例。

(3)抛出异常。

一旦Java抛出了异常,方法不会被返回调用者,因此不必担心返回值的问题,也不必提供一个缺省的返回值。

下面举一个例子,如代码10-16所示。

【代码10-16】  异常抛出示例1

public class ExceptionUse {

     //可能会抛出异常类的方法

     public static void throwException() throws IoException {

          System.out.println("下面产生一个Io异常并将其抛出!");

          //抛出异常

          throw new RuntimeException("MyException");

     }

     public static void main(String [] args) {

          try {

               throwException();

          }

          //捕获异常

          catch(IoException re) {

               System.out.println("捕获Io异常:"+re);

          }

     }

}

程序的输出结果如图10-13所示。

 

http://book.csdn.net/BookFiles/319/img/image018.jpg

图10-13  异常抛出示例1运行结果

代码10-16在main()函数中调用了throwException()方法,从此方法的声明中可以看到,它可能会抛出IoException异常。再看此方法的定义,可以看到,它使用new运算符实例化了一个RuntimeException 类,并使用throw语句将其抛出。调用这个方法后,异常被抛出,catch语句进行捕获,并打印出异常信息。

再例如,在10.2.1小节中的代码10-4的方法testTryCatch()中,捕获并处理了异常IOException和IllegalAccessException。然而,另外一种选择是在方法testTryCatch()中并不处理此异常,而将异常留给这个方法的调用者去处理。这时,需要在方法testTryCatch()的throws子句中指明这个异常。将代码10-4更改以后如代码10-17所示。

【代码10-17】  异常抛出示例2

public class ExceptionUse{

    public static void main(String[] args) {

         try{

             testTryCatch();

         }

         //捕获异常

         catch(IOException e){

              System.out.println("catch "+e.getMessage()+" in main()");

         }

    }

    static void testTryCatch() throws IOException{

         try{

             //抛出异常

             throw new IOException("exception 1");

         }

         //捕获异常

         catch(IOException e){

              System.out.println("throw "+e.getMessage()+"

                                                        in testTryCatch()");

              //捕获异常暂不处理,抛出异常

              throw new IOException("exception 1");

        }           

    }

}  

程序的输出结果如图10-14所示。

 

http://book.csdn.net/BookFiles/319/img/image019.jpg

图10-14  异常抛出示例2运行结果

注意:程序中,在方法声明中抛出异常static void testTryCatch() throws IOException,因此,应由外层方法来处理(在main()方法中使用try-catch块来捕获异常),如果main()这个外层调用方法不使用try-catch块来捕获异常,程序将无法通过编译。

10.3.2  Throwable 类和它的子类

在Java语言中的所有的异常类都是从Throwable类派生出来的。Throwable类就是Java提供的最有效的异常处理类。继承Throwable类的对象包括直接子类(直接继承于Throwable类的对象)和间接子类(继承于Throwable类的子类的对象),如图10-15所示。

 

http://book.csdn.net/BookFiles/319/img/image020.gif

图10-15  Throwable 类和它的子类

由图10-15可以看到,Throws有两个直接的子类:Error类和Exception类。

(1)Error类。Error类及其子类主要用来描述一些Java运行时刻系统内部的错误或资源枯竭导致的错误,如当在Java虚拟机中发生动态连接失败或其他的定位失败的时候,Java虚拟机抛出一个Error对象。普通的简易程序不捕获或抛出Errors对象,也就是说,普通的程序不能从这类错误中恢复。

(2)Exception类。Exception类和它的子类与Error类不同,它是普通程序可以从中恢复的所有标准异常的超类。大多数程序都抛出或捕获衍生于Exception类的对象。一个异常表明发生了一个问题,但它不是严重的系统问题。大多数程序将会抛出或捕获Exceptions对象(而不是Errors对象)。

在Java平台中Exception类有许多已经定义了的子类。这些子类说明所发生的异常的各种类型。例如,IllegalAccessException异常类说明了不能找到一个特殊的方法;NegativeArraySizeException异常类说明程序试图创建一个带有负尺寸的数组。

具体的说,Exception类有两个分支:从RuntimeException中派生出来的类和不是从RuntimeException类中派生的类,这样分类的根据是错误发生的原因。

RuntimeException是程序员编写程序不正确所导致的异常;而其他的异常则是由于一些异常的情况造成的,不是程序本身的错误,如果这些异常情况没有发生,程序本身仍然是好程序。

RuntimeException是在程序运行期间在Java虚拟机内部所发生的异常。例如NullPointerException类就是一个运行时异常类,在一个方法试图通过一个null引用来访问一个对象的成员时会发生这个异常。具体的说,RuntimeException类的错误有错误的强制类型转换、数组越界访问、空指针操作。其他异常(非RuntimeException类的异常)则包括文件指针越界、格式不正确的URL、试图为一个不存在的类找一个代表它的Class类的对象。

RuntimeException类的异常的产生是程序员的过失,理论上,程序员经过检查和测试可以查出这类错误。

抱歉!评论已关闭.