您的位置:寻梦网首页编程乐园Java天地Core JavaJava基础教程
Java 天地

Java基础教程

第一章 Java的产生与特点
第二章 Java程序编译与运行环境
第三章 Java程序设计基础
第四章 Java应用程序的基本结构
第五章 Java的类
第六章 Java图形用户接口
第七章 Java的多线程
第八章 Java的“异常”
第九章 Java的输入输出操作

第八章 Java的“异常”

“异常”指的是程序运行时出现的非正常情况。 在用传统的语言编程时, 程序员只能通过函数的返回值来发出错误信息。 这易于导致很多错误, 因为在很多情况下需要知道错误产生的内部细节。 通常, 用全局变量errno来存储“异常”的类型。 这容易导致误用, 因为一个errno的值有可能在被处理之前被另外的错误覆盖掉。 即使最优美的C语言程序, 为了处理“异常”情况, 也常求助于goto语句。 Java对“异常”的处理是面向对象的。 一个Java的Exception是一个描述“异常”情况的对象。 当出现“异常”情况时, 一个Exception对象就产生了, 并放到产生这个“异常”的成员函数里。


8.1 基础

Java的“异常”处理是通过5个关键词来实现的: try, catch, throw, throws和finally。 用try 来执行一段程序, 如果出现“异常”, 系统抛出(throws)一个“异常”, 你可以通过它的类型来捕捉(catch)它, 或最后(finally)由缺省处理器来处理。 下面是“异常”处理程序的基本形式:

  try { 
    //程 序 块 
  } catch (ExceptionType1 e) { 
    // 对ExceptionType1的处理 
  } catch (ExceptionType2 e) {
    // 对ExceptionType2的处理
    throw(e); //再抛出这个“异常”
  } finally { } 

8.2 "异常”的类型

在“异常”类层次的最上层有一个单独的类叫做Throwable。 这个类用来表示所有的“异常”情况。 每个“异常”类型都是Throwable的子类。 Throwable有两个直接的子类。 一类是Exception, 是用户程序能够捕捉到的“异常”情况。 我们将通过产生它的子类来创建自己的“异常”。 另一类是Error, 它定义了那些通常无法捕捉到的“异常”。 要谨慎使用Error子类, 因为它们通常会导致灾难性的失败。 在Exception中有一个子类RuntimeException, 它是程序运行时自动地对某些错误作出反应而产生的。


8.3 不捕捉“异常”

“异常”对象是Java在运行时对某些“异常”情况作出反应而产生的。 例如, 下面这个小程序包含一个整数被0除的“异常”。

  class Exc0 { 
    public static void main(String args[]) {
      int d = 0; 
      int a = 42/d;
    }
  }

当Java执行这个除法时, 由于分母是0, 就会构造一个“异常”对象来使程序停下来并处理这个错误情况, 在运行时“抛出”(throw) 这个“异常”。 说“抛出”是因为它象一个滚烫的马铃薯, 你必须把它抓住并立即处理。 程序流将会在除号操作符处被打断, 然后检查当前的调用堆栈来查找“异常”。 一个“异常”处理器是用来立即处理“异常”情况的。 在这个例子里, 我们没有编一个“异常”处理器, 所以缺省的处理器就发挥作用了。 缺省的处理器打印Exception的字符之值和发生“异常”的地点。 下面是我们的小例子的输出。

C:>java Exc0
java.lang.arithmeticException: / by zero at Exc0.main(Exc0.java:4)


8.4 try与catch

通常我们希望自己来处理“异常”并继续运行。 可以用try来指定一块预防所有“异常”的程序。 紧跟在try程序后面, 应包含一个catch子句来指定你想要捕捉的“异常”的类型。 例如, 下面的例子是在前面的例子的基础上构造的, 但它包含一个try程序块和一个catch子句。

  class exc1 {
    public static void main(string args[]) {
      try {
        int d = 0; 
        int a = 42 / d; 
      } catch (arithmeticexception e) {
        system.out.println("division by zero"); 
      }
    }
  }

catch子句的目标是解决“异常”情况, 把一些变量设到合理的状态, 并象没有出错一样继续运行。 如果一个子程序不处理某个“异常”, 则返到上一级处理, 直到最外一级。


8.5 多个catch子句

在某些情况下, 同一段程序可能产生不止一种“异常”情况。 你可以放置多个catch子句, 其中每一种“异常”类型都将被检查, 第一个与之匹配的就会被执行。 如果一个类和其子类都有的话, 应把子类放在前面, 否则将永远不会到达子类。 下面是一个有两个catch子句的程序的例子。

  class MultiCatch { 
    public static void main(String args[]) {
      try { 
        int a = args.length; 
        System.out.println("a = " + a);
        int b = 42/a; 
        int c[] = {1};
        c[42] = 99; 
      } catch(ArithmeticException e) { 
        System.out.println("div by 0: " + e); 
      } catch(ArrayIndexOutOfBoundsException e) {
        system.out.println("array index oob: " + e);
      }
    }
  }

如果在程序运行时不跟参数, 将会引起一个0做除数的“异常”, 因为a的值为0。 如果我们提供一个命令行参数, 将不会产生这个“异常”, 因为a的值大于0。 但会引起一个ArrayIndexOutOfBoundexception的“异常”, 因为整型数组c的长度是1, 却给c[42]赋值。 下面是以上两种情况的运行结果。

C:>java MultiCatch
a = 0
div by 0: java.lang.arithmeticexception: / by zero

C:>java MutiCatch 1
a = 1
array index oob: java.lang.ArrayIndexOutOfBoundsException:42


8.6 try语句的嵌套

你可以在一个成员函数调用的外面写一个try语句, 在这个成员函数内部, 写另一个try语句保护其他代码。 每当遇到一个try语句,“异常”的框架就放到堆栈上面, 直到所有的try语句都完成。 如果下一级的try语句没有对某种“异常”进行处理, 堆栈就会展开, 直到遇到有处理这种“异常”的try语句。 下面是一个try语句嵌套的例子。

  class MultiNest { 
    static void procedure() { 
      try { 
        int c[] = { 1 };
        c[42] = 99;
      } catch(ArrayIndexOutOfBoundsexception e) {
        System.out.println("array index oob: " + e);
      }
    }
    public static void main(String args[]) {
      try { 
        int a = args.length; 
        system.out.println("a = " + a);
        int b = 42/a; procedure();
      } catch(arithmeticException e) {
        System.out.println("div by 0: " + e);
      }
    }
  }

成员函数procedure里有自己的try/catch控制, 所以main不用去处理ArrayIndexOutOfBoundsException。


8.7 throw语句

throw语句用来明确地抛出一个“异常”。 首先, 你必须得到一个Throwable的实例的控制柄, 通过参数传到catch子句, 或者用new操作符来创建一个。 下面是throw语句的通常形式。

  throw ThrowableInstance; 

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

  class ThrowDemo { 
    static void demoproc() { 
      try {
        throw new NullPointerException("de3mo"); 
      } catch(NullPointerException e) { 
        System.out.println("caught inside demoproc"); 
        throw e; 
      }
    }
    public static void main(String args[]) { 
      try { 
        demoproc();
      } catch(NullPointerException e) {
        system.out.println("recaught: " + e);
      }
    }
  }

8.8 throws语句

throws用来标明一个成员函数可能抛出的各种“异常”。对大多数Exception子类来说, Java 编译器会强迫你声明在一个成员函数中抛出的“异常”的类型。 如果“异常”的类型是Error或 RuntimeException, 或它们的子类, 这个规则不起作用, 因为这些在程序的正常部分中是不期待出现的。 如果你想明确地抛出一个RuntimeException, 你必须用throws语句来声明它的类型。 这就重新定义了成员函数的定义语法:

  type method-name(arg-list) throws exception-list{ } 

下面是一段程序, 它抛出了一个“异常”, 但既没有捕捉它, 也没有用throws来声明。 这在编译时将不会通过。

  class ThrowsDemo1 {
    static void procedure( ) {
      System.out.println("inside procedure");
      throw new IllegalAccessException("demo"); 
    }
    public static void main(String args[]) { 
      procedure( ); 
    }
  }

为了让这个例子编译过去, 我们需要声明成员函数procedure抛出了IllegalAccessException, 并且在调用它的成员函数main里捕捉它。 下面是正确的例子:

  class ThrowsDemo { 
    static void procedure( ) throws IllegalAccessException {
      System.out.println("inside procedure"); 
      throw new IllegalAccessException("demo");
    }
    public static void main(String args[]) { 
      try { 
        procedure( );
      } catch (IllegalAccessException e) { 
        System.out.println("caught " + e);
      }
    }
  }

下面是输出结果:

C:>java ThrowsDemo
inside procedure
caught java.lang.IllegalAccessException: demo


8.9 finally

当一个“异常”被抛出时, 程序的执行就不再是线性的, 跳过一些行, 甚至会由于没有与之匹配的catch子句而过早地返回。 有时确保一段代码不管发生什么“异常”都被执行到是必要的, 关键词finally就是用来标识这样一段代码的。 即使你没有catch子句, finally程序块也会在执行try程序块后的程序之前执行。 每个try语句都需要至少一个与之相配的catch子句或finally子句。 一个成员函数返回到调用它的成员函数, 或者通过一个没捕捉到的“异常”, 或者通过一个明确的return语句, finally子句总是恰好在成员函数返回前执行。 下面是一个例子, 它有几个成员函数, 每个成员函数用不同的途径退出, 但执行了finally子句。

  class FinallyDemo {
    static void procA( ) {
      try {
        System.out.println("inside procA");
        throw new RuntimeException("demo");
      } finally { 
        System.out.println("procA`s finally");
      }
    }
    static void procB( ) {
      try { 
        System.out.println("inside procB");
        return; 
      } finally { 
        System.out.println("procB`s finally");
      }
    }
    public static void main(String args[]) { 
      try { 
        procA( ); 
      } catch (Exception e);
      procB( );
    }
  }

下面是这个例子的运行结果:

C:>java FinallyDemo
inside procA
procA`s finally
inside procB
procB`s finally


本章小结

1. “异常”指的是程序运行时出现的非正常情况。
2. 在“异常”类层次的最上层的类叫Throwable, 它有两个直接的子类: Exception和Error。
3. Java的“异常”处理通过5个关键词来实现: try, catch, throw, throws和finally。


(作者: 刘春阳  来源: 不详)