1. 基本异常

异常情形是指阻止当前方法或作用域继续执行的问题。异常情形与普通问题区分在于普通问题是指在当前环境下能得到足够的信息,总能处理这个错误,而对于异常情形,就不能继续下去了,所能做的就是从当前环境跳出,并且把问题提交给上一级环境。

当抛出异常后,Java将使用new在堆上创建异常对象的引用,此时异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行,这个恰当的地方就是异常处理程序,它的任务是将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去。

比如,对于对象引用t,有可能在使用时尚未被初始化,所在在使用这个对象引用调用其方法之前,会先对引用进行检查,例如:

if (t == null)
  throw new NullPointerException();

这就抛出了异常,它将在别的地方得到处理。异常使我们可以将每件事当作一个事务考虑,而异常可以看护这些事务的底线,我们在程序中可以拥有各种不同的恢复点,如果程序的某部分失败了,异常将恢复到程序中某个已知的稳定点上。

所有标准异常类都有两个构造器,一个是默认构造器,另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器,例如:

throw new NullPointerException("t = null");

在使用new创建了异常对象之后,此对象的引用将传给throw,尽管返回的异常对象其类型通常与方法设计的返回类型不同,但从效果上看,它就像是从方法返回的。此外,能够抛出任意类型的Throwable对象,它是异常类型的根类。


2. 捕获异常

如果在方法内部抛出了异常,这个方法将在抛出异常的过程中结束。如果不希望方法就此结束,可以在方法内设置一个特殊的块来捕获异常,它是跟在try关键字之后的普通程序块,例如:

try {
  // your code
}

有了异常处理机制,可以把所有动作都放在try块里,然后只需在一个地方就可以捕获所有异常。抛出的异常必须在某处得到处理,以关键字catch表示,例如:

try {
  // your code
} catch (Type1 id1) {
  // Handle exception of Type1
} catch (Type2 id2) {
  // Handle exception of Type2
}

每个catch子句接收一个且仅接收一个特殊类型的参数的方法,它必须紧跟在try块之后,当异常被抛出时,异常处理机制将负责搜寻参数与异常类型匹配的第一个处理程序,然后进入catch子句执行。


3. 自定义异常

要自己定义异常类,必须从已有的异常类继承,例如:

class Simple Exception extends Exception {}

还可以更进一步自定义异常,比如加入构造器和成员,例如:

class SimpleException extends Exception {
  private int x;
  public SimpleException(String msg) {
    super(msg);
  }
}

如果把所有可能会抛出的异常告知使用此方法的客户端程序员,它可以使调用者能确切知道写什么样的代码可以捕获所有潜在的异常,如果提供了源代码,客户端程序员可以在源代码中查找throw语句来获知相关信息。但是程序库通常并不与源代码一起发布,为了预防这样的问题,Java提供了相应的语法告知客户端程序员某个方法可能会抛出的异常类型,这就是异常说明,它属于方法声明的一部分,紧跟在形式参数列表之后。

异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表,例如:

void f() throws TooBig, TooSmall {
  // your code
}

代码必须与异常说明保持一致,如果方法里的代码产生了异常却没有进行处理,编译器会发出提醒,要么处理这个异常,要么就在异常说明中表明此方法将产生异常。


4. 捕获所有异常

可以只写一个异常处理程序来捕获所有类型的异常,通过捕获异常类型的基类Exception就可以做到这一点,例如:

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

因为Exception是所有异常类的基类,所以它不会含有太多具体的信息,不过可以调用它从其基类Throwable继承的方法getMessage()和getLocalizedMessage()用来获取详细信息,或用本地语言表示的详细信息。打印Throwable和Throwable的调用栈轨迹可以用printStackTrace(),调用栈显示了带到异常抛出点的方法调用序列,fillInStackTrace()用于在Throwable对象的内部记录栈桢的当前状态。

Throwable这个类用来表示任何可以作为异常被抛出的类,它可分为两种类型:Errow用来表示编译时和系统错误,Exception是可以被抛出的基本类型,Java程序员关心的基类型通常是Exception。


5. 使用finally进行清理

对于一些代码,可能会希望无论try块中的异常是否抛出,它们都能得到执行,可以在异常处理程序后面加上finally子句,例如:

try {
} catch(A a1) {
  // Handler for situation A
} catch(B b1) {
  // Handler for situation B
} finally {
  // Activities that happen every time
}

对于没有垃圾回收和析构函数自动调用机制的语言来说,finally能使程序员保证无论try块里发生了什么,内存总能得到释放,但是Java有垃圾回收机制。那么在Java中,当要把除内存之外的资源恢复到它们的补始状态时,就要用到finally子句,比如已经打开的文件或网络连接,或屏幕上画的图形等等。