Java 异常处理的 5 个关键技巧

85 阅读4分钟

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)

    • 受检查异常:在编译期就需要处理的异常(如IOExceptionSQLException),要么用try-catch块捕获,要么在方法声明中用throws关键字抛出,否则编译无法通过。

    • 非受检查异常:继承自RuntimeException的异常(如ArithmeticExceptionNullPointerException),编译期不强制处理,通常是由程序逻辑错误导致的,需要通过修正代码来避免。

Error(错误)

  • 同样是Throwable的子类,但它代表系统级别的错误,通常是由虚拟机生成的,超出了程序开发者的控制范围,如OutOfMemoryError(内存溢出)、StackOverflowError(栈溢出)等。

  • 对于Error,程序不应该尝试处理,因为这类错误往往意味着程序无法继续正常运行,此时应让程序终止并由系统进行处理。

嵌套 try-catch 块的使用场景

嵌套try-catch块允许在一个trycatch块内部再包含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 程序,有效应对运行时的各种意外情况。