Java异常机制,是程序运行不正常时的处理方式。具体来说,异常机制提供了程序安全退出的通道。当异常发生时,程序的执行流程改变,程序控制权转移到异常处理器。
一、Java异常继承体系
Java异常类的继承体系如下图所示:
Throwable类是整个Java异常体系的超类,包括错误Error和异常Exception两个子类。
- Error表示程序在运行期间出现了十分严重、不可恢复的错误,这种情况下应用程序只能终止运行,比如JVM出现错误(VirtualMachineError)。应用程序一般不捕获Error,因为它是应用程序所无法处理的情况。
- Exception是应用程序可以处理的异常,可以选择对其进行捕获并处理,包含运行时异常(RuntimeException)和非运行时异常(除RuntimeException外的其他异常)。
- 运行时异常RuntimeException是在程序运行时出现的异常,一般是由程序的逻辑错误所引起的,可以选择捕获处理,也可以不捕获处理。比如NullPointerException、IndexOutOfBoundsException等。
- 非运行时异常也叫受检异常,必须在应用程序中对其进行捕获处理或者向上抛出,否则无法通过编译器检查。比如IOException、ClassNotFoundException等。方法内部如果抛出了受检异常,则必须在方法头部声明抛出该受检异常类型。
用户可以在在Java异常体系的基础上自定义类异常类型,自定义异常类型要遵循以下原则:
- 自定义异常类必须是Throwable的子类。
- 如果想自定义一个受检异常类,需要继承Exception类。
- 如果想自定义一个运行时异常类,需要继承RuntimeException类。
二、异常的抛出与捕获处理
1.异常抛出(throw、throws)
throw关键字用于方法体内部,用来抛出异常。如果抛出的是一个受检异常,那么还应该在方法头部用throws声明抛出的受检异常类型,该方法的调用者也必须捕获处理或者向上抛出这个受检异常。比如:
public class className { |
2.异常捕获处理(try…catch…finally)
try代码块中某位置抛出了异常,则该位置之后的代码再也没有机会被执行。
catch可以有多个,一个try代码块后面跟随多个catch代码块的情况叫多重捕获,根据异常的类型从上往下匹配,如果有匹配的catch,下面的所有catch会被全部忽略。
多重捕获下要先catch子类异常再catch父类异常,即子类异常的catch块在父类异常catch块的上面。如果顺序反了的话,那么根据Java继承思想,所有子类异常对象都是父类异常对象,导致父类异常永远先被捕获到,而捕获子类异常的catch块永远得不到执行,出现编译错误。比如下面的代码:(ArithmeticException是Exception的子类,属于受检异常)
public class ExceptionErrorDemo { |
无论try代码块中是否发生异常,finally代码块都会被执行。通常可以在finally块中进行资源的回收工作,比如关闭打开的文件、删除临时文件等。
如果try、catch块中有return语句,会先把返回值压栈,然后再执行finally语句,执行完finally语句后再返回。即使在finally中更改了要返回的变量的值,程序依然会返回之前已经压栈的值,而不是在finally中更新后的值。当然,如果try、catch中返回的变量是一个对象引用而非基本类型,由于引用已经压栈无法更改,可以通过在finally块中更改引用指向的对象的内容,来影响返回结果。如果finally中有return语句,那么直接忽略try、catch中的return语句,只执行finally的return。