【Java】 异常

异常分类

异常层次结构

  1. 所有的异常都是由Throwable继承而来,但在下一层理解分解为两个类Error和Exception。
  2. Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。应用程序不应该跑出这种类型的对象。如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力。
  3. 在设计Java程序时,需要关注Exception层次,这个层次结构又分解为两个分支:一个分支派生于RuntimeException;另一个分支包含其他异常。划分两个分支的规则是:由程序错误导致的异常属于RuntimeException;而程序本身没有问题,但由于I/O错误这类问题导致的异常属于其他异常。
  4. 派生于RuntimeException的异常包含下面几种情况:
  • 错误的派生类型
  • 数组访问越界
  • 访问空指针
    不是派生于RuntimeException的异常包括:
  • 试图在文件尾部后面读取数据
  • 试图打开一个不存在的文件
  • 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在。
  1. Java语言规范将派生于Error类或RuntimeException类的所有异常称为未检查(unchecked)异常,所有其他的异常称为已检查(checked)异常。

声明已检查的异常

方法应该在其首部声明所有可能出现抛出的异常。这样可以从首部反映出这个方法可能抛出哪类已检查异常。

public FileInputStream(String name) throws FileNotFoundException
  1. 在编写自己的方法时,不必将所有可能抛出的异常都进行声明。置于什么时候需要在方法中用throws子句声明异常,什么异常必须使用throws子句声明,需要记住在下面4种情况时应该抛出异常:
    1)调用一个抛出已检查异常的方法,例如,FileInputStream构造器。
    2)程序运行过程中发现错误,并且利用throw语句抛出一个已检查异常
    3)程序出现错误,例如,a[-1]=0会抛出一个ArrayIndexOutOfBoundsException这样的未检查异常
    4)Java虚拟机和运行时库出现的内部错误
    如果出现了前两种情况之一,则必须告诉调用这个方法的程序员有可能抛出异常。因为任何一个抛出异常的方法都有可能是一个死亡陷阱。如果没有处理器捕获这个异常,当前执行的线程就会结束。
  2. 对于那些可能被他人使用的Java方法,应该根据异常规范(Exception specification),在方法的首部声明这个方法可能抛出的异常。
class MyAnimation {
    public Image loadImage(String s) throws IOException {
        ...
    }
}

如果一个方法有可能抛出多个已检查异常,那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔。如:

class MyAnimation {
    poblic Image loadImage(String s) throws FileNotFoundException, EOFException {
        ...
    }
}

但是,不需要声明Java的内部错误,即从Error继承的错误。任何程序代码都具有抛出那些异常的潜能,而我们对其没有任何控制能力。
同样,也不应该声明从RuntimeException继承的那些未检查异常。

class MyAnimation{
    void drawImage(int i) throws ArrayIndexOutOfBoundsException { //bad style
        ...
    }
}

这些运行时错误完全在我们的控制之下。如果特别关注数组下标引发的错误,就应该讲更多的时间花费在修正程序中的错误上,而不是说明这些错误发生的可能性上。
总之,一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。如果方法没有声明所有可能发生的已检查异常,编译器就会给出一个错误消息。

捕获异常

如果某个异常发生的时候没有进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈的内容。要想捕获一个异常,必须设置try/catch语句块。最简单的try语句块如下所示:

try {
    code
    more code
    more code
} catch (ExceptionType e) {
    handler for this type
}

如果在try语句块中的任何代码抛出了一个在catch自娱中说明的异常类,那么
1)程序将跳过try语句块的其余代码。
2)程序将执行catch字句中的处理器代码。
如果在try语句块中的代码没有抛出任何异常,那么程序将跳过catch子句。
如果方法中的任何代码抛出了一个在catch子句中没有声明的异常类型,那么这个方法就会立刻退出。
通常捕获哪些知道如何处理的异常,而将那些不知道如何处理的异常继续进行传递。如果想传递一个异常,就必须在方法的首部添加一个throws说明符,以便告知调用者这个方法可能会抛出异常。

捕获多个异常

在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。可以按照下列方式为每个异常类型使用一个单独的catch子句:

try {
    code that might throw exceptions
} catch (FileNotFoundException e) {
    emergency action for missing files
} catch (UnknownHostException e) {
    emergency action for unknown hosts
} catch (IOException e) {
    emergency action for all other I/O problems
}

在Java SE7中,同一个catch子句中可以捕获多个异常类型。例如,假设对应缺少文件和异常未知主机异常的动作是一样的,就可以合并catch子句:

try {
    code that might throw exception
} catch (FileNotFoundException  | UnknownHostException e) {
    emergency action for missing files and unknown hosts
} catch (IOException e) {
    emergency action for all other I/O problems
}

只有当捕获的异常类型彼此之间不存在子类关系时才需要这个特性。

捕获所有异常

可以只写一个异常处理程序来捕获所有类型的异常。通过捕获异常类型的基类Exception,就可以做到这一点(事实上还有其他的基类,但Exception是同编程活动相关的基类):

catch(Exception e) {
    System.out.println("Caught an exception");

这将捕获所有异常,所以最好把它放在处理程序表的末尾,以防它抢在其他处理程序之前就把异常捕获了。

再次抛出异常与异常链

在catch子句中可以抛出一个异常,这样做的目的是改变异常的类型。如果开发了一个供其他程序员使用的子系统,那么,用于表示子系统故障的异常类型可能会产生多种解释。ServletException就是这样一个异常类型的例子。执行servlet的代码可能不想知道发射给你错误的细节原因,但希望明确地知道servlet是否有问题。
下满给出了捕获异常并将它再次抛出的基本方法:

try {
    access the database;
} catch (SQLException e) {
    throw new ServletException("..") 
}

finally子句

不管是否有异常被捕获,finally子句中的代码都被执行

posted @ 2017-01-07 16:25  vincently  阅读(241)  评论(0编辑  收藏  举报