try 块
try块是异常处理的起点,用于包裹可能会抛出异常的代码。当try块中的代码执行时,如果发生异常,程序会立即跳转到对应的catch块进行处理,而try块中异常发生点之后的代码则不会再执行。例如在之前的除法示例中,int ans = n / m;这行代码可能抛出异常,将其放在try块中,就能及时捕获并处理。
catch 块
catch块用于捕获并处理try块中抛出的特定类型的异常。一个try块可以跟随多个catch块,以处理不同类型的异常,且catch块的顺序很重要 —— 子类异常需要放在父类异常之前,否则子类异常的catch块会被父类异常的catch块覆盖,无法得到正确执行。
每个catch块都需要指定要捕获的异常类型,如catch (ArithmeticException e)表示捕获算术异常,变量e则包含了异常的详细信息,通过e.getMessage()等方法可以获取异常描述。
finally 块
finally块是可选的,它通常跟随在catch块之后,用于执行无论是否发生异常都必须运行的代码,比如资源释放(关闭文件、数据库连接等)。无论try块中是否抛出异常,也无论catch块是否执行,finally块中的代码一定会被执行。例如在操作文件时,即使读取文件过程中发生异常,关闭文件的操作也应该放在finally块中,确保资源不被泄露。
异常的分类与特点
根据 Java 异常体系,异常主要分为以下两类:
Exception(异常)
-
是
Throwable的子类,代表程序运行过程中可以预测且能够被处理的错误。 -
可分为受检查异常(Checked Exception) 和非受检查异常(Unchecked Exception):
-
受检查异常:在编译期就需要处理的异常(如
IOException、SQLException),要么用try-catch块捕获,要么在方法声明中用throws关键字抛出,否则编译无法通过。 -
非受检查异常:继承自
RuntimeException的异常(如ArithmeticException、NullPointerException),编译期不强制处理,通常是由程序逻辑错误导致的,需要通过修正代码来避免。
-
Error(错误)
-
同样是
Throwable的子类,但它代表系统级别的错误,通常是由虚拟机生成的,超出了程序开发者的控制范围,如OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)等。 -
对于
Error,程序不应该尝试处理,因为这类错误往往意味着程序无法继续正常运行,此时应让程序终止并由系统进行处理。
嵌套 try-catch 块的使用场景
嵌套try-catch块允许在一个try或catch块内部再包含try-catch结构,适用于需要在不同层级处理异常的场景。例如,当处理复杂的业务逻辑时,内层操作可能抛出一种异常,而外层操作在处理内层结果时可能抛出另一种异常。
以之前的示例为例:
public class NestedTryExample {
public static void main(String\[] args) {
try {
System.out.println("Outer try block");
try {
int a = 10 / 0; // 抛出ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Inner catch: " + e); // 处理内层异常
}
String str = null;
System.out.println(str.length()); // 抛出NullPointerException
} catch (NullPointerException e) {
System.out.println("Outer catch: " + e); // 处理外层异常
}
}
}
运行结果为:
Outer try block
Inner catch: java.lang.ArithmeticException: / by zero
Outer catch: java.lang.NullPointerException
可以看到,内层异常被内层catch处理后,程序继续执行外层try块的后续代码,直到遇到新的异常,再由外层catch处理,保证了程序在不同层级异常发生时仍能有序执行。
异常处理的最佳实践
-
避免过度捕获异常:不要用一个
try-catch块捕获所有异常(如catch (Exception e)),应针对性地处理特定异常,便于定位问题。 -
及时释放资源:将资源释放操作放在
finally块中,确保资源不会因异常而未被释放。 -
不要忽略异常:避免空的
catch块(即不做任何处理),至少应记录异常信息,便于排查问题。 -
合理使用
throws:当方法无法处理异常时,用throws将异常抛给上层调用者,由更合适的层级处理。
通过合理运用异常处理机制,开发者可以编写更健壮、更易维护的 Java 程序,有效应对运行时的各种意外情况。