大可制作:QQ群:31564239(asp|jsp|php|mysql)

Java Gossip: 异常的继承架构

Java的异常处理机制并不是只有将程序逻辑与异常处理分开的好处,程序设计的错误情况很多且难以估计,并没有人能保证自已所设计的程序完全无误,异常处理最重要的是为程序设计人员提供种种可能的异常情况,让程序设计人员能够掌握并设法排除。

Java编译器会检查程序语法等的相关错误,这些错误是属于“编译时期错误”,然而语法无误并不代表程序逻辑没有错误,逻辑上的错误会在程序执行时发生,这是属于“运行时错误”,而即使逻辑没有错误,也可能因为I/O、网路或甚至内存不足等情况而发生错误。

Java所处理的异常主要可分为两大类,一种是严重的错误,例如硬体错误或内存不足等问题,与此相关的类是位于java.lang下的Error类;另一种是非严重的错误,代表可以处理的状况,与此相关的是位于java.lang下的Exception类

Error类与Exception类都继承自Throwable类,Throwable类拥有几个报告相关异常讯息的方法:


getLocalizedMessage() 目前对象的描述
getMessage() 取得对象的错误讯息
printStackTrace() 取得堆中的讯息


除了使用这些方法之外,我们也可以利用toString()取得异常对象的错误描述。

您所处理的异常通常都是衍生自Exception类,其中大部份是运行时异常(RuntimeException), 例如 ArithmeticException、ArrayIndexOutOfBoundsException等等,另外还有一些非运行时异常,例如 ClassNotFoundException(尝试载入类时失败所引发,例如类文件不存在)、InterruptedException(线程非 执行中而尝试中断所引发的异常)等等, 以下列出一些重要的继承架构:
Throwable
  Error(严重的系统错误)
    LinkageError
    ThreadDeath
    VirtualMachineError
    ....
  Exception
    ClassNotFoundException
    CloneNotSupportedException
    IllegalAccessException
    ....
    RuntimeException(运行时异常)
      ArithmeticException
      ArrayStoreException
      ClassCastException
      ....

属于RuntimeException衍生出来的类,是在运行时会发生的,不需要特别使用try-catch或是在函数上使用"throws"声明也 可以通过编译,例如您在使用数组时,并不一定要处理ArrayIndexOutOfBoundsException异常。

Exception下非RuntimeException衍生之异常如果有引发的可能性,则您一定要在程序中明确的指定处理才可以通过编译,例如当您使用 到BufferedReader类时,由于有可能引发IOException,您要不就在try-catch中处理,要不就在函数上使用throws表 示由调用它的函数来处理。

了解异常处理的继承架构是必须的,例如在捕捉异常对象时,如果父类异常对象撰写在子类异常对象之前被捕捉,则catch子类异常对象的区块将永远不会被执行,事实上编译器也会帮您检查这个错误,例如:

  • UseException.java
import java.io.*; 

public class UseException {
public static void main(String[] args) {
try {
throw new ArithmeticException("异常测试");
}
catch(Exception e) {
System.out.println(e.toString());
}
catch(ArithmeticException e) {
System.out.println(e.toString());
}
}
}

这个程序若在编译时将会产生以下的错误讯息:
UseException.java:11: exception java.lang.ArithmeticException has already been caught
catch(ArithmeticException e) {
^
1 error

要完成这个程序的编译,您必须更改异常对象捕捉的顺序,例如:

  • UseException.java
import java.io.*; 

public class UseException {
public static void main(String[] args) {
try {
throw new ArithmeticException("异常测试");
}
catch(ArithmeticException e) {
System.out.println(e.toString());
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}

执行结果:
java.lang.ArithmeticException: 异常测试 

在撰写程序时,您也可以如上将Exception异常对象的捕捉撰写在最后,以便捕捉到所有尚未考虑到的异常,并进一步改进程序。

如果您要自订自已的异常类,您可以继承Exception类而不是Error,Error是属于严重的系统错误,您不用去处理它,您也可以继承 RuntimeException类,就如之前所说过的,这个异常不一用明确使用try-catch来处理也可以通过编译,但通常建议的是继承 Exception,至少这样的程序会是比较安全的,对于可处理的这些异常,您在程序中必须明确的解决它,如果没有,编译器会告诉您。

一些程序会自行继承相关的异常类,包括一些相关的异常讯息,它们也会在定义接口(interface)时于方法上声明throws某些类型的异常,然而 如果您在这些方法中发生了某些不是方法声明的异常(可能由于使用的底层技术不同而有这种情况),您就无法将之throw,只能自行撰写一些try.. catch来暗自处理掉,如果想要让这些异常丢出至上层,就要更多道的手续了,例如撰写一个类继承RuntimeException,在发生异常时将该异常包装至这个RuntimeException类中,然后再丢出。