6.2 异常类
6.2 异常类
NullPointerException和NumberFormatException都是异常类,所有异常类都有一个共同的父类Throwable,我们先来介绍这个父类,然后介绍Java中的异常类体系,最后介绍怎么自定义异常。
6.2.1 Throwable
NullPointerException和NumberFormatException有一个共同的父类Throwable,它有4个public构造方法:
1 | 1. public Throwable() |
Throwable类有两个主要参数:一个是message,表示异常消息;另一个是cause,表示触发该异常的其他异常。异常可以形成一个异常链,上层的异常由底层异常触发,cause表示底层异常。Throwable还有一个public方法用于设置cause:
1 | Throwable initCause(Throwable cause) |
Throwable的某些子类没有带cause参数的构造方法,就可以通过这个方法来设置,这个方法最多只能被调用一次。在所有构造方法的内部,都有一句重要的函数调用:
1 | fillInStackTrace(); |
它会将异常栈信息保存下来,这是我们能看到异常栈的关键。Throwable有一些常用方法用于获取异常信息,比如:
1 | void printStackTrace() //打印异常栈信息到标准错误输出流 |
6.2.2 异常类体系
以Throwable为根,Java定义了非常多的异常类,表示各种类型的异常,部分类如图6-1所示。
Throwable是所有异常的基类,它有两个子类:Error和Exception。
Error表示系统错误或资源耗尽,由Java系统自己使用,应用程序不应抛出和处理,比如图6-1中列出的虚拟机错误(VirtualMacheError)及其子类内存溢出错误(OutOfMemory-Error)和栈溢出错误(StackOverflowError)。
Exception表示应用程序错误,它有很多子类,应用程序也可以通过继承Exception或其子类创建自定义异常,图6-1中列出了三个直接子类:IOException(输入输出I/O异常)、RuntimeException(运行时异常)、SQLException(数据库SQL异常)。
RuntimeException比较特殊,它的名字有点误导,因为其他异常也是运行时产生的,它表示的实际含义是未受检异常(unchecked exception),相对而言,Exception的其他子类和Exception自身则是受检异常(checked exception),Error及其子类也是未受检异常。
受检(checked)和未受检(unchecked)的区别在于Java如何处理这两种异常。对于受检异常,Java会强制要求程序员进行处理,否则会有编译错误,而对于未受检异常则没有这个要求。下文我们会进一步解释。
RuntimeException也有很多子类,表6-1列出了其中常见的一些。
如此多不同的异常类其实并没有比Throwable这个基类多多少属性和方法,大部分类在继承父类后只是定义了几个构造方法,这些构造方法也只是调用了父类的构造方法,并没有额外的操作。
那为什么定义这么多不同的类呢?主要是为了名字不同。异常类的名字本身就代表了异常的关键信息,无论是抛出还是捕获异常,使用合适的名字都有助于代码的可读性和可维护性。
6.2.3 自定义异常
除了Java API中定义的异常类,也可以自己定义异常类,一般是继承Exception或者它的某个子类。如果父类是RuntimeException或它的某个子类,则自定义异常也是未受检异常;如果是Exception或Exception的其他子类,则自定义异常是受检异常。
我们通过继承Exception来定义一个异常,如代码清单6-2所示。
1 | public class AppException extends Exception { |
和很多其他异常类一样,我们没有定义额外的属性和代码,只是继承了Exception,定义了构造方法并调用了父类的构造方法。