ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 9.3 标准Java异常 Java包含了一个名为`Throwable`的类,它对可以作为异常“抛”出的所有东西进行了描述。`Throwable`对象有两种常规类型(亦即“从`Throwable`继承”)。其中,`Error`代表编译期和系统错误,我们一般不必特意捕获它们(除在特殊情况以外)。`Exception`是可以从任何标准Java库的类方法中“抛”出的基本类型。此外,它们亦可从我们自己的方法以及运行期偶发事件中“抛”出。 为获得异常的一个综合概念,最好的方法是阅读由`http://java.sun.com`提供的联机Java文档(当然,首先下载它们更好)。为了对各种异常有一个大概的印象,这个工作是相当有价值的。但大家不久就会发现,除名字外,一个异常和下一个异常之间并不存在任何特殊的地方。此外,Java提供的异常数量正在日益增多;从本质上说,把它们印到一本书里是没有意义的。大家从其他地方获得的任何新库可能也提供了它们自己的异常。我们最需要掌握的是基本概念,以及用这些异常能够做什么。 ``` java.lang.Exception ``` 这是程序能捕获的基本异常。其他异常都是从它派生出去的。这里要注意的是异常的名字代表发生的问题,而且异常名通常都是精心挑选的,可以很清楚地说明到底发生了什么事情。异常并不全是在`java.lang`中定义的;有些是为了提供对其他库的支持,如`util`,`net`以及`io`等——我们可以从它们的完整类名中看出这一点,或者观察它们从什么继承。例如,所有IO异常都是从`java.io.IOException`继承的。 ## 9.3.1 `RuntimeException`的特殊情况 本章的第一个例子是: ``` if(t == null) throw new NullPointerException(); ``` 看起来似乎在传递进入一个方法的每个引用中都必须检查`null`(因为不知道调用者是否已传递了一个有效的引用),这无疑是相当可怕的。但幸运的是,我们根本不必这样做——它属于Java进行的标准运行期检查的一部分。若对一个空引用发出了调用,Java会自动产生一个`NullPointerException`异常。所以上述代码在任何情况下都是多余的。 这个类别里含有一系列异常类型。它们全部由Java自动生成,毋需我们亲自动手把它们包含到自己的异常规范里。最方便的是,通过将它们置入单独一个名为`RuntimeException`的基类下面,它们全部组合到一起。这是一个很好的继承例子:它建立了一系列具有某种共通性的类型,都具有某些共通的特征与行为。此外,我们没必要专门写一个异常规范,指出一个方法可能会“抛”出一个`RuntimeException`,因为已经假定可能出现那种情况。由于它们用于指出编程中的错误,所以几乎永远不必专门捕获一个“运行期异常”——`RuntimeException`——它在默认情况下会自动得到处理。若必须检查`RuntimeException`,我们的代码就会变得相当繁复。在我们自己的包里,可选择“抛”出一部分`RuntimeException`。 如果不捕获这些异常,又会出现什么情况呢?由于编译器并不强制异常规范捕获它们,所以假如不捕获的话,一个`RuntimeException`可能过滤掉我们到达`main()`方法的所有途径。为体会此时发生的事情,请试试下面这个例子: ``` //: NeverCaught.java // Ignoring RuntimeExceptions public class NeverCaught { static void f() { throw new RuntimeException("From f()"); } static void g() { f(); } public static void main(String[] args) { g(); } } ///:~ ``` 大家已经看到,一个`RuntimeException`(或者从它继承的任何东西)属于一种特殊情况,因为编译器不要求为这些类型指定异常规范。 输出如下: ``` java.lang.RuntimeException: From f() at NeverCaught.f(NeverCaught.java:9) at NeverCaught.g(NeverCaught.java:12) at NeverCaught.main(NeverCaught.java:15) ``` 所以答案就是:假若一个`RuntimeException`获得到达`main()`的所有途径,同时不被捕获,那么当程序退出时,会为那个异常调用`printStackTrace()`。 注意也许能在自己的代码中仅忽略`RuntimeException`,因为编译器已正确实行了其他所有控制。因为`RuntimeException`在此时代表一个编程错误: (1) 一个我们不能捕获的错误(例如,由客户程序员接收传递给自己方法的一个空引用)。 (2) 作为一名程序员,一个应在自己的代码中检查的错误(如`ArrayIndexOutOfBoundException`,此时应注意数组的大小)。 可以看出,最好的做法是在这种情况下异常,因为它们有助于程序的调试。 另外一个有趣的地方是,我们不可将Java异常划分为单一用途的工具。的确,它们设计用于控制那些讨厌的运行期错误——由代码控制范围之外的其他力量产生。但是,它也特别有助于调试某些特殊类型的编程错误,那些是编译器侦测不到的。