处理错误
假设Java程序在运行期间出现了一个错误,该错误可能是文件包含了错误信息、网络连接出现问题、使用无效的数组下标、使用一个没有被赋值的对象造成的,在出现这些错误时,程序应该做到其中一点:
- 返回到一种安全状态,并能够让用户执行一些其他的命令;
- 允许用户保存所有操作的结果,并以妥善的方式终止程序。
异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。
程序中可能出现的错误有以下几种:
- 用户输入错误:可能是键盘输入错误,也可能是URL输入错误;
- 设备错误:可能硬件出现问题;
- 物理限制:磁盘满了,可用存储空间已用完;
- 代码错误:可能程序语法错误或数组越界等等。
对于方法中的一个错误,传统的做法是返回一个错误码,但并不适用于任何情况。在这种情况下,可以抛出一个封装了错误信息的对象,然后让程序退出,调用这个方法的代码也无法执行,取而代之的是异常处理机制开始搜索能够处理这种异常状况的异常处理器(exception handler)。
异常分类
所有异常对象都是继承自Throwable类的一个实例,用户也可以创建自己的异常类。异常继承结构如下图:
Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误,应用程序不应该抛出这种类型的对象,出现这种错误,程序只能通告给用户并尽力使程序安全终止。
由程序错误导致的异常属于RuntimeException,而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。
派生于RuntimeException的异常包含以下几种情况:
- 错误的类型转换;
- 数组访问越界;
- 访问null指针。
不是派生于RuntimeException的异常包括:
- 试图在文件尾部后面读取数据;
- 试图打开一个不存在的文件;
- 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在。
如果出现RuntimeException异常,那么就一定是你的问题。可以通过检测数组下标是否越界来避免ArrayIndexOutOfBoundException异常。而像文件不存在这种异常是取决于环境的。
Java语言规范将派生于Error类或RuntimeException类的所有异常称为非受查异常,所有其他的异常称为受查异常,编译器将核查是否为所有的受查异常提供了异常处理器。
声明受查异常
如果遇到了无法处理的情况,Java的方法可以抛出一个异常。例如要读取的文件不存在,就会抛出一个IOException异常。
可以在方法名后面抛出一个异常,这个方法会运行内部的代码,遇到异常时,就会抛出。如果真的抛出了这个异常,那运行时系统就会开始搜索异常处理器,以便知道如何处理异常对象。
以下几种情况应该抛出异常:
- 调用一个抛出受查异常的方法,例如FileInputStream构造器;
- 程序运行过程中发现错误,并且利用throw语句跑出一个受查异常;
- 程序出现错误,会抛出一个非受查异常;
- Java虚拟机和运行时库出现的内部错误。
前两种情况都需要告诉方法可能抛出异常。
如果在子类中覆盖了超类的一个方法,子类方法中声明的受查异常不能比超类方法中声明的异常更通用。如果超类方法没有抛出异常,子类也不能抛出任何异常。
捕获异常
如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印异常信息,要想捕获一个异常,必须设置try/catch块。如果try块中的代码抛出了一个在catch中的异常类,那么:
- 程序将跳过try语句块中的其它代码;
- 程序将执行catch中的异常处理器代码。
如果在try块中的代码没有抛出任何异常,那么程序将跳过catch子句。如果方法中的任何代码抛出了一个在catch字句中没有声明的异常,这个方法会立即退出。
一个try块可以捕获多个异常,每个catch捕获一个异常,也可以一个catch捕获多个异常,用|分隔,但只能捕获其中一个,且异常之前不可以有继承关系。
在catch块中可以抛出一个异常来改变异常,以便明确的知道异常信息。
finally子句
finally子句执行的三种情况:
- 代码没有抛出异常,先执行try中的全部代码,再执行finally中的代码,再执行之后的代码;
- 抛出一个catch中捕获的异常,这种情况下,跳过try语句块中的其他代码,执行catch中的代码,然后执行finally中的代码;
- 代码抛出了一个异常,但这个异常不是由catch捕获的,这种情况下,将执行try中所有语句,直到有异常抛出,然后跳过其余代码,执行finally中的代码。
try可以只有finally,而没有catch。
带资源的try语句
可以在try后加入一个资源参数,当try块退出时,会自动调用资源的close()方法。这样可以避免该资源关闭产生的异常影响原来的异常。
堆栈轨迹(stack trace)
堆栈轨迹是一个方法调用过程的列表,它包含了程序执行中方法调用的特定位置,可以调用Throwable类的printStackTrace方法访问堆栈轨迹的文本描述信息。也可以用getStackTrace方法得到stackTraceElement对象的一个数组,StackTraceElement类含有获取文件名和当前执行的代码行号的方法,以及获取类名和方法名的方法。