异常
异常
Exception 和 Error 区别
所有异常都有一个共同的祖先:Throwable
类。该类有两个重要的子类:
Exception
:程序本身可以处理的异常,可以通过catch
来进行捕获。Exception
又可以分为:- Checked Exception (受检查异常,必须处理)
- Unchecked Exception (不受检查异常,可以不处理)。
Error
:Error
属于程序无法处理的错误 ,我们没办法通过catch
来进行捕获不建议通过catch
捕获 。例如- Java 虚拟机运行错误(
Virtual MachineError
) - 虚拟机内存不够错误(
OutOfMemoryError
) - 栈溢出错误(
StackOverFlowError
) - 类定义错误(
NoClassDefFoundError
)
这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
- Java 虚拟机运行错误(
Checked Exception 和 Unchecked Exception 区别
Checked Exception 即 受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 catch
或者throws
关键字处理的话,就没办法通过编译。
比如下面这段 IO 操作的代码:
RuntimeException = UncheckedException,可以不用 throw 也不用 try 包裹
RuntimeException and its subclasses are unchecked exceptions. Unchecked exceptions do not need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.
NullPointerException(空指针异常)、
IndexOutOfBoundsException(下标越界异常)、
ClassCastException(类转换异常)、
ArrayStoreException(数据存储异常,操作数组时类型不一致)、
IO 操作的 BufferOverflowException 异常;
RuntimeException 以外的异常,是非运行时异常、 checked exceptions 编译异常,是类型上都属于 Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如 IOException、SQLException 等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。
运行时异常(RuntimeException)是 Java 中的一种异常类型,它通常是由程序错误或逻辑错误引起的。与受检查异常(Checked Exception)不同,运行时异常不需要在方法签名中声明,也不需要在调用方法时显式地捕获或抛出。
以下是一些常见的运行时异常及其出现的场景:
NullPointerException(空指针异常):当尝试访问一个空对象的属性或方法时,会抛出 NullPointerException 异常。例如,当一个对象为 null 时,调用它的方法或属性就会抛出空指针异常。
ArrayIndexOutOfBoundsException(数组下标越界异常):当尝试访问数组中不存在的元素时,会抛出 ArrayIndexOutOfBoundsException 异常。例如,当尝试访问数组中的负数下标或超出数组长度的下标时,就会抛出数组下标越界异常。
ClassCastException(类型转换异常):当尝试将一个对象强制转换为不兼容的类型时,会抛出 ClassCastException 异常。例如,当尝试将一个字符串对象转换为整数类型时,就会抛出类型转换异常。
IllegalArgumentException(非法参数异常):当传递给方法的参数不合法或不正确时,会抛出 IllegalArgumentException 异常。例如,当传递给方法的参数为 null 或超出有效范围时,就会抛出非法参数异常。
IllegalStateException(非法状态异常):当对象处于不正确的状态时,会抛出 IllegalStateException 异常。例如,当尝试在未初始化的对象上调用方法时,就会抛出非法状态异常。
需要注意的是,这些异常通常是由程序员的错误或逻辑错误引起的,因此在编写程序时应该尽可能避免这些异常的出现。
异常处理
在Java中,try-catch-finally块的执行顺序是按照以下方式进行的:
首先,执行try块中的代码。
如果try块中的代码没有发生异常,则跳过catch块,直接执行finally块中的代码。
如果try块中的代码发生了异常,那么异常会被抛出,并且会按照代码中定义的顺序进行异常处理。
首先,会检查catch块中是否有与抛出的异常类型匹配的catch语句。
如果找到匹配的catch语句,则执行该catch块中的代码,并且在catch块执行完后,再执行finally块中的代码。
如果没有找到匹配的catch语句,则异常会被传递给上层调用栈中的异常处理机制,直到找到匹配的catch块或者程序终止。
最后,不论是否发生异常,无论是否有catch块被执行,finally块中的代码都会被执行。
总结起来,try块中的代码会被优先执行,如果发生异常,则会执行匹配的catch块中的代码,然后执行finally块中的代码。如果没有发生异常,则直接执行finally块中的代码。无论是否发生异常,finally块中的代码都会被执行。
return
如果在try块中的代码中存在return语句,那么在执行return语句之前,会先执行finally块中的代码。然后,finally块中的代码执行完毕后,才会执行return语句。
finally 里面可以写 return,但是一定会执行,一旦写了,那么 try 和 catch 块里的 return 语句都会失效
如果finally块中也存在return语句,那么无论try块中的代码是否存在return语句,最终返回的值将是finally块中的return语句的返回值。这是因为finally块中的return语句会覆盖try块中的return语句。
finally 中的代码一定会执行吗?
finally块中的代码基本一定会被执行,除非在try块中使用了System.exit()方法导致程序终止。在这种情况下,finally块中的代码将不会被执行。
就比如说 finally 之前虚拟机被终止运行的话,finally 中的代码就不会被执行。
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
// 终止当前正在运行的Java虚拟机
System.exit(1);
} finally {
System.out.println("Finally");
}
输出:
Try to do something
Catch Exception -> RuntimeException
另外,在以下 2 种特殊情况下,finally
块的代码也不会被执行:
- 程序所在的线程死亡。
- 关闭 CPU。
然而,有一些特殊情况下finally块可能不会被执行。以下是一些可能导致finally块不执行的情况:
- 在try块中使用了System.exit()方法导致程序终止。在这种情况下,finally块中的代码将不会被执行。
- 在try块或catch块中发生了无限循环或死循环,导致程序无法正常结束。在这种情况下,finally块中的代码将不会被执行。
- 在try块或catch块中使用了Thread.stop()方法导致线程被强制终止。在这种情况下,finally块中的代码也将不会被执行。