在 Java 编程中,异常处理是一个至关重要的部分,它能够帮助我们构建更加健壮和稳定的应用程序。本文将深入探讨 Java 异常的相关知识,包括异常类层次结构、不同类型异常的区别、常用方法以及异常处理的方式等。
一、异常类层次结构
在 Java 中,所有的异常都有一个共同的祖先 ——java.lang包中的Throwable类。Throwable类有两个重要的子类:Exception和Error。
(一)Exception
Exception表示程序本身可以处理的异常,可以通过catch来进行捕获。它又可以进一步分为Checked Exception(受检查异常,必须处理)和Unchecked Exception(不受检查异常,可以不处理)。
(二)Error
Error属于程序无法处理的错误,我们没办法通过catch来进行捕获,也不建议这样做。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等。当这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
二、Checked Exception 和 Unchecked Exception 的区别
(一)Checked Exception
即受检查异常,Java 代码在编译过程中,如果受检查异常没有被catch或者throws关键字处理的话,就没办法通过编译。例如 IO 操作相关的代码,如果文件不存在引发FileNotFoundException,就必须进行处理。除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常。常见的受检查异常有:IO 相关的异常、ClassNotFoundException、SQLException等。
(二)Unchecked Exception
即不受检查异常,Java 代码在编译过程中,即使不处理不受检查异常也可以正常通过编译。RuntimeException及其子类都统称为非受检查异常,常见的有:NullPointerException(空指针错误)、IllegalArgumentException(参数错误比如方法入参类型错误)、NumberFormatException(字符串转换为数字格式错误,IllegalArgumentException的子类)、ArrayIndexOutOfBoundsException(数组越界错误)、ClassCastException(类型转换错误)、ArithmeticException(算术错误)、SecurityException(安全错误比如权限不够)、UnsupportedOperationException(不支持的操作错误比如重复创建同一用户)等。
三、Throwable 类常用方法
String getMessage():返回异常发生时的简要描述。String toString():返回异常发生时的详细信息。String getLocalizedMessage():返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同。void printStackTrace():在控制台上打印Throwable对象封装的异常信息。
四、try - catch - finally 的使用
(一)try 块
用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
(二)catch 块
用于处理try捕获到的异常。
(三)finally 块
无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。需要注意的是,不要在finally语句块中使用return,当try语句和finally语句中都有return语句时,try语句块中的return语句会被忽略。
以下是一个简单的代码示例:
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
} finally {
System.out.println("Finally");
}
输出结果为:
Try to do something
Catch Exception -> RuntimeException
Finally
不过,在某些特殊情况下,finally中的代码不会被执行,比如在finally之前虚拟机被终止运行,或者程序所在的线程死亡,以及关闭 CPU 等情况。
五、try - with - resources
适用范围为任何实现java.lang.AutoCloseable或者java.io.Closeable的对象。在try - with - resources语句中,任何catch或finally块在声明的资源关闭后运行。相比于try - catch - finally,它能让代码更简短、更清晰,产生的异常也更有用。例如读取文件内容的操作,使用try - with - resources可以简化代码:
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
六、异常使用的注意事项
- 不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次手动抛出异常,都需要手动
new一个异常对象抛出。 - 抛出的异常信息一定要有意义。
- 建议抛出更加具体的异常,比如字符串转换为数字格式错误的时候应该抛出
NumberFormatException而不是其父类IllegalArgumentException。 - 避免重复记录日志,如果在捕获异常的地方已经记录了足够的信息(包括异常类型、错误信息和堆栈跟踪等),那么在业务代码中再次抛出这个的异常时,就不应该再次记录相同的错误信息。重复记录日志会使得日志文件膨胀,并且可能会掩盖问题的实际原因,使得问题更难以追踪和解决。
通过对 Java 异常的深入理解和正确使用,我们能够更好地处理程序运行过程中可能出现的各种问题,提高程序的可靠性和稳定性。

被折叠的 条评论
为什么被折叠?



