在 Java 编程语言中,所有异常的根类是 java.lang 包下的 Throwable 类。它的两个主要子类分别是:
Exception:程序可以处理的异常,这些异常可以通过catch语句来捕获。Exception类进一步分为 Checked Exception(受检查异常,必须处理)和 Unchecked Exception(不受检查异常,可以选择不处理)。Error:此类包含程序无法处理的错误,建议不要通过catch语句来捕获这些错误。例如 Java 虚拟机运行错误(VirtualMachineError)、内存不足错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等。发生这些错误时,一般情况下,Java 虚拟机(JVM)会选择终止线程。
Checked Exception 和 Unchecked Exception 的区别
Checked Exception:受检查异常。在 Java 编译过程中,如果未通过 catch 或 throws 关键字处理受检查异常,代码无法通过编译。
例如,以下 IO 操作的代码:

除了 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块会在方法返回之前被执行。
代码示例:
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 块中使用 return! 当 try 语句和 finally 块中都有 return 语句时,try 块中的 return 语句会被忽略。
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());
System.exit(1); // 终止当前 JVM
} finally {
System.out.println("Finally");
}
输出结果:
Try to do something
Catch Exception -> RuntimeException
此外,有以下两种特殊情况下,finally 块的代码也不会被执行:
- 程序所在的线程死亡。
- CPU 被关闭。
如何使用 try-with-resources 代替 try-catch-finally?
- 适用范围(资源的定义): 任何实现
java.lang.AutoCloseable或java.io.Closeable接口的对象。 - 关闭资源和 finally 块的执行顺序: 在
try-with-resources语句中,任何catch或finally块在声明的资源关闭后运行。
《Effective Java》中指出:
面对必须关闭的资源时,总是应该优先使用
try-with-resources而不是try-finally,这样生成的代码更简洁、更清晰,异常处理也更有效。
在 Java 中,类似 InputStream、OutputStream、Scanner、PrintWriter 等资源都需要调用 close() 方法手动关闭。以下是通过 try-catch-finally 语句实现的代码:
// 读取文本文件的内容
Scanner scanner = null;
try {
scanner = new Scanner(new File("D://read.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
使用 Java 7 之后的 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();
}
当然,在需要关闭多个资源时,try-with-resources 使其变得非常简单,而使用 try-catch-finally 可能会引发许多问题。
异常使用的注意事项
- 不要将异常定义为静态变量,因为这会导致异常栈信息错乱。每次手动抛出异常时,必须手动创建一个新的异常对象。
- 抛出的异常信息必须具备意义。
- 建议抛出更具体的异常,例如在字符串转换为数字格式错误时,应抛出
NumberFormatException而不是其父类IllegalArgumentException。 - 使用日志打印异常后,避免再次抛出异常(两者不应同时出现在同一逻辑中)。