目录
所有的异常都是由Throwable类继承而来,Throwable又分为了Error类和Exception类
一、Error
Java运行时系统的内部错误和资源耗尽错误
表示比较严重的问题,一般是JVM运行时出现了错误,如没有内存可分配抛出OOM错误、栈资源耗尽抛出StackOverflowError错误、Java虚拟机运行错误Virtual MachineError、类定义错误NoClassDefFoundError。
如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止之外,再也无能为力了。
二、Exception
异常又分为RuntimeException和其他异常
由程序错误导致的异常属于RuntimeException,而程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常
2.1 运行时异常RuntimeException
顾名思义,运行时才可能抛出的异常,编译器不会处理此类异常。比如数组索引越界、使用的对象为空、强制类型转换错误、除0等等。出现了运行时异常,一般是程序的逻辑有问题,是程序自身的问题而非外部因素
2.2 其他异常
Exception中除了运行时异常之外的,都属于其他异常,也可以称为编译时异常,这部分异常编译器必须处置。这部分异常往往是因为外部运行环境导致,因为程序可能运行在各种环境中。编译器要求Java程序必须捕获或声明所有的编译时异常,强制要求程序为可能出现的异常做准备工作。
不要被运行时异常的名称所迷惑,理论上所有的错误都是运行时发生的。包括Error、RuntimeException、编译时 异常等等。所有的这些都只能在程序运行的过程中才能碰到。编译时异常指的是编译器要求必须处理的异常,并不是代码编译间发生的错误。
2.3 受检异常和非受检异常
接受检查的异常和不接受检查的异常
Error和RuntimeException运行时异常都不能被检查。他们都是程序运行过程中所产生的,只是 Error的错误比较严重,一般是JVM产生,而RuntimeException⼀般是程序逻辑自身的问题。
受检异常就是编译时异常,这些异常在编译时被强制要求捕获或者声明。编译器将会为所有的受检异常提供异常处理器。
2.4 异常处理机制
主要是try-catch-finally和throw、throws关键字
受检异常只有两种选择。要么被捕获处理,要么被抛出,让调用者处理。
2.4.1 try-catch-finally
try-catch-finally用来捕获异常
try中放可能存在异常的方法
1、如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,那么程序将跳过try语句块的其余代码,并且执行catch子句中的处理器代码
2、如果try语句块中的代码没有抛出任何异常,那么程序将跳过catch子句
3、如果方法中的任何代码抛出了catch子句中没有声明的异常类型,那么这个方法就会立刻退出
finally一般用来关闭所占用的资源。如果代码抛出异常,就会终止剩余代码的处理,并且退出这个方法。这样可能会导致一些程序占用的系统不能被正确的释放,而不管是否有异常被捕获,finally中子句的代码都会被执行,可以在这里正确的释放资源。
但是如果try或者catch中出现了System.exit()语句,则会直接退出,不会执行finally模块。
1、finally中没有return语句 最后返回值为2
int i = 1;
try {
i = 2;
return i;
} catch (Exception e) {
} finally {
i = 3;
}
2、 finally中有return语句 最后返回值为3
int i=1;
try {
i = 2;
return i;
} catch (Exception e) {
} finally {
i = 3;
return i;
}
上⾯两个代码只是在finally中是否存在return语句的区别,但直接结果却并不相同。
可以看到如果finally中没有return语句,程序就会把finally中的操纵数据忽略掉。
其实finally中的数据操作也是执行了的,但是并没有返回。这是因为在return语句返回之前,虚拟机会将待返回的值压入操作数栈,等待返回。即使 finally 语句块对 i 进行了修改,但是待返回的值已经确实的存在于操作数栈中了,所以不会影响程序返回结果。
在try中的数据处理完,检测到有return语句时,会先将数据压入操作数栈等待返回,然后去执行finally语句,如果 finally语句没有将新的结果压入操作数栈,那么只可能返回原先的结果。
从这个角度理解,即使finally中对数据处理,但是返回的依旧时try中的“脏数据”。
finally并不是没有执行,而是执行了却没有没返回。添加return语句则会将finally处理过的数据压入操作数栈返回,原先的“脏数据”失效。
2.4.2 throws
throws用来声明异常,关注点是受检异常
2.4.3 抓抛模型
try-catch-finally是抓,throw/throws是抛。一个类遇到异常,要么捕获后处理,要么继续向上抛出,让调用者进行处理。
catch中可以为空,不进行处理本身就是一种处理方式。
try-catch-finally可以灵活组合
可以try-catch、try-finally或者try-catch-finally
1、try中执行正常,忽略catch,最后执行finally。
2、try中出现异常,且异常在catch中声明,执行catch,最后执行finally。
3、try中出现异常,但catch中未声明,不执行catch,最后执行finally。
4、try中出现异常,且异常在catch中声明,执行catch时出现异常,停止catch中代码,执行finally并且抛出catch 中新出现的异常。
三、Throwable类
主要方法:
1、public String getMessage():返回关于发生的异常的详细信息
2、public Throwable getCause():返回一个Throwable对象代表异常原因
3、public String toString():使用getMessage()的结果返回类的串级名字
四、常见面试题
4.1 Exception和Error有什么区别?
两者都派生自Throwable类的子类
1、Exception类及其子类主要用于表示程序可以处理的异常情况。异常分为两种类型:受检异常和不受检异常。程序员可以选择捕获并处理异常,也可以通过在方法签名中使用throws关键字声明方法可能抛出的异常
2、Error类及其子类通常表示虚拟机无法恢复的严重错误。错误不应该由应用程序捕获和处理,因为它们通常表示虚拟机或系统本身的问题。一般情况下,程序员不会直接捕获和处理Error。这些错误的发生通常意味着程序无法继续正常执行
4.2 受检异常和非受检异常有什么区别?
1、受检异常是编译时异常,程序员必须处理或声明这些异常。如果不处理,编译器将报错。通常派生自Exception类及其子类,但不派生自RuntimeException
2、非受检异常是运行时异常,通常是由编程错误导致的。这些异常通常派生自RuntimeException或其子类。程序员可以选择处理这些异常,但不是强制的
4.3 如何处理异常?
try-catch-finally
try:包裹业务代码
catch:捕获和处理异常
finally:回收资源
try {
// 代码块,可能会抛出异常
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {
// 处理 ExceptionType2 类型的异常
} finally {
// ⽆论是否发⽣异常,都会执⾏的代码块
}
4.4 finally总是会被执行吗?
一般来说,finally块都会在try或catch块执行完毕后被执行,即使发生了异常。然而,有一些情况下,finally块可能不会执行,主要是在以下情况:
1、在try或catch块中调用了System.exit():导致Java虚拟机(JVM)退出
try {
// ⼀些代码
System.exit(0); // 这会导致JVM退出,finally块不会执⾏
} finally {
// 这⾥的代码不会执⾏
}
2、在try块中发生了死循环
try {
while (true) {
// ⼀些代码
}
} finally {
// 这⾥的代码可能⽆法执⾏
}