什么是异常
在程序执行过程中发生的不正常行为或非预期行为,称为异常。
通过以上两个例子可以发现,当程序产生异常时,异常之后的代码不再执行;产生的错误代码原因不同,抛出的错误名称也不相同,因此会有对应的"异常类"的对象产生。异常在Java中也是类。
异常的体系结构
如图所示
1、Throwable这个类是Java异常的最顶层父类,继承自这个类的子类,当发生一切特殊场景时,由JVM来自动产生该类的对象并传递给程序(这就是异常对象)
2、Error及其子类:这类异常一旦发生,当前程序时是无法解决的,属于JVM内部错误,只能退出程序,检查错误并修改代码。比如栈溢出Error、堆溢出Error。
3、Exception及其子类:这种异常属于程序开发过程中,可以人为写代码进行异常处理的异常类型,当发生异常后,程序可以捕获改类异常,使得异常发生之后,正常的代码可以继续执行。
异常的处理流程
异常的捕获与处理
三个关键字:try、catch、finally
try{
//存放可能出现异常的代码
}catch(异常类型 异常对象){
//出现该异常后如何处理
}finally{
//无论是否产生异常,最终都会执行改代码
}
1、try…catch代码块
a、若try中出现异常,有与之相关的catch块捕获,程序处理异常之后,恢复执行直到结束
b、若catch捕获的异常类型与try中产生的异常类型不相同时,程序还是会退出。
c、当try中产生的异常有多种类型时,可以使用多个catch代码块进行捕获处理。
在这种情况下,出现多个catch块时,程序只会进入一个catch块中处理。
d、由于异常也是类,也存在继承关系,可以用catch来捕获Exception类型的异常。
在这种情况中,若先定义父类的catch块,则子类的catch永远不会执行!!
e、当异常捕获时,可以调用异常类提供的printStackTrace方法来输出异常的信息。
2、finally代码块
无论是否有异常产生,都一定会执行的代码块。比如在文件、网络、数据库中,会将资源的关闭操作放在finally中,无论程序是否出错,保证资源正确关闭。
同时,若finally代码块中存在return语句,无论是否有异常产生,finally中的返回值都会覆盖try或者catch中的返回值,所以一般在finally中不写返回值。
异常的抛出
上面讲的出现异常之后,在出现异常的位置进行捕获并处理,还有另外一种方式,就是沿着程序的调用链,进行异常传递。
1、throws关键字:用于方法声明,表示当前方法可能产生异常,且当前方法不处理该异常,交给调用者处理。
若方法中产生了多种异常,可以在方法声明处,声明多个异常类型,但在实际抛出时,和catch类似,只会抛出碰到的第一个异常类型。
2、throw关键字:用在方法内部,表示人为进行异常对象的产生,当异常对象产生时,就相当于此处发生了改类型的异常。一般throw搭配自定义的异常来使用。
受查异常和非受查异常
1、红色框及其子类都属于受查异常,指的是在程序编译阶段,若产生了受查异常,必须要进行异常处理的异常类,称为受查异常。(Exception及其子类)
以CloneNotSupportedException为例:
2、蓝色框以及其子类都属于非受查异常,程序编译阶段碰到非受查异常,可以不进行异常处理,运行时出现问题再解决。(RuntimeException及其子类)
自定义异常
虽然JDK提供了各种各样的异常类,但是实际开发中会有很多的业务异常(例如,用户名错误,密码错误等问题)。因此需要根据实际场景来自定义异常类。要自定义异常类,只需要继承JDK中的异常父类即可。
若继承Exception,则当前异常救属于受查异常;若继承RuntimeException,则当前异常类属于受查异常。
示例:自定义用户名和密码错误的异常类