在Java中,异常主要分为三种:Exception,RuntimeException以及Error。这三类异常都是Throwable的子类。直接从Exception派生的各个异常类型就是我们刚刚提到的Checked Exception。它的一个比较特殊的地方就是强制调用方对该异常进行处理。就以我们常见的用于读取一个文件内容的FileReader类为例。在该类的构造函数声明中声明了其可能会抛出FileNotFoundException:
public FileReader(String fileName) throws FileNotFoundException {
……
}
那么在调用该构造函数的函数中,我们需要通过try…catch…来处理该异常:
public void processFile() {
try {
FileReader fileReader = new FileReader(inFile);
} catch(FileNotFoundException exception) {
// 异常处理逻辑
}
……
}
如果我们不通过try…catch…来处理该异常,那么我们就不得不在函数声明中通过throws标明该函数会抛出FileNotFoundException:
public void processFile() throws FileNotFoundException {
FileReader fileReader = new FileReader(inFile); // 可能抛出FileNotFoundException
……
}
而RuntimeException类的各个派生类则没有这种强制调用方对异常进行处理的需求。为什么这两种异常会有如此大的区别呢?因为RuntimeException所表示的是软件开发人员没有正确地编写代码所导致的问题,如数组访问越界等。而派生自Exception类的各个异常所表示的并不是代码本身的不足所导致的非正常状态,而是一系列应用本身也无法控制的情况。例如一个应用在尝试打开一个文件并写入的时候,该文件已经被另外一个应用打开从而无法写入。对于这些情况,Java通过Checked Exception来强制软件开发人员在编写代码的时候就考虑对这些无法避免的情况的处理,从而提高代码质量。
而Error则是一系列很难通过程序解决的问题。这些问题基本上是无法恢复的,例如内存空间不足等。在这种情况下,我们基本无法使得程序重新回到正常轨道上。因此一般情况下,我们不会对从Error类派生的各个异常进行处理。而且由于其实际上与本文无关,因此我们不再对其进行详细讲解。