Java 异常处理详解

Java 异常处理详解(2025–2026 现代实践版)

Java 异常处理是 Java 语言的核心机制之一,用于处理程序运行时的错误或异常情况,确保程序的健壮性可维护性。异常处理可以避免程序崩溃、记录错误信息、恢复程序状态或优雅退出。
本文从基础概念 → 核心语法 → 进阶技巧 → 最佳实践 全流程详解,结合代码示例和生产经验。基于 Java 21+(LTS 版本),但兼容旧版。

1. 异常基础概念

  • 什么是异常
    异常(Exception)是程序运行时发生的异常事件(如除零、网络断开、文件不存在),它中断了正常的执行流程。Java 将异常视为对象,所有异常都继承自 Throwable 类。
  • 异常层次结构(关键类图):
顶级类子类示例描述
ThrowableError严重错误(如 OutOfMemoryError),不可恢复,不建议捕获
Exception可恢复异常,分两类:Checked 和 Unchecked
ExceptionChecked Exception编译时检查,必须处理(如 IOException、SQLException)
Unchecked Exception运行时异常,无需强制处理(如 NullPointerException、ArrayIndexOutOfBoundsException),继承自 RuntimeException
  • Checked vs Unchecked(面试高频):
  • Checked:必须在方法签名用 throws 声明,或用 try-catch 处理。适合可预见、可恢复的异常(如文件读写)。
  • Unchecked:无需声明,适合编程错误(如空指针)。生产中,Unchecked 异常往往表示 bug,应优先避免。

2. 核心语法:try-catch-finally

  • 基本结构
  try {
      // 可能抛出异常的代码
  } catch (ExceptionType1 e) {
      // 处理异常1
  } catch (ExceptionType2 e) {
      // 处理异常2(多 catch 块,按顺序匹配,先子类后父类)
  } finally {
      // 无论是否异常,都执行(资源释放神器,如关闭连接)
  }
  • 示例1:简单捕获(Unchecked 异常):
  public class BasicException {
      public static void main(String[] args) {
          try {
              int[] arr = new int[2];
              System.out.println(arr[3]);  // 抛出 ArrayIndexOutOfBoundsException
          } catch (ArrayIndexOutOfBoundsException e) {
              System.out.println("数组越界:" + e.getMessage());
          } finally {
              System.out.println("finally 总会执行");
          }
      }
  }

输出:

  数组越界:Index 3 out of bounds for length 2
  finally 总会执行
  • 示例2:多 catch + Checked 异常
  import java.io.FileReader;
  import java.io.IOException;

  public class CheckedExample {
      public static void main(String[] args) {
          try {
              FileReader fr = new FileReader("nonexistent.txt");  // 抛出 FileNotFoundException
          } catch (IOException e) {  // 父类捕获
              System.out.println("IO 异常:" + e);
          } catch (Exception e) {    // 更广义捕获(放最后)
              System.out.println("通用异常:" + e);
          } finally {
              System.out.println("清理资源");
          }
      }
  }
  • Java 7+ 新特性:try-with-resources(自动关闭资源):
  try (FileReader fr = new FileReader("file.txt")) {
      // 使用 fr
  } catch (IOException e) {
      // 处理
  }  // 无需 finally,fr 自动 close()

3. throw 和 throws

  • throw:手动抛出异常对象。
  • throws:方法签名声明可能抛出的 Checked 异常,交给调用者处理。
  • 示例
  public class ThrowExample {
      public static void checkAge(int age) throws IllegalArgumentException {  // throws 声明
          if (age < 0) {
              throw new IllegalArgumentException("年龄不能为负!");  // throw 抛出
          }
          System.out.println("年龄正常:" + age);
      }

      public static void main(String[] args) {
          try {
              checkAge(-1);
          } catch (IllegalArgumentException e) {
              System.out.println(e.getMessage());
          }
      }
  }

输出:年龄不能为负!

4. 自定义异常

  • 为什么自定义?标准异常无法表达业务语义时(如“余额不足”)。
  • 步骤:继承 Exception(Checked)或 RuntimeException(Unchecked)。
  • 示例(Unchecked 自定义):
  // 自定义异常类
  public class InsufficientBalanceException extends RuntimeException {
      public InsufficientBalanceException(String message) {
          super(message);
      }
  }

  // 使用
  public class BankAccount {
      private double balance = 100;

      public void withdraw(double amount) {
          if (amount > balance) {
              throw new InsufficientBalanceException("余额不足,仅剩 " + balance);
          }
          balance -= amount;
      }
  }

5. 进阶技巧

  • 异常链(Exception Chaining):捕获低级异常,包装成高级异常抛出,保留原始原因。
  try {
      // 低级操作
  } catch (LowLevelException e) {
      throw new HighLevelException("高级错误", e);  // 包装
  }
  • 好处:e.getCause() 可追溯根源。
  • 异常抑制(Suppressed Exceptions):try-with-resources 中,如果 close() 也抛异常,用 e.getSuppressed() 获取。
  • 异步异常:多线程中,异常不会跨线程传播。需在 Runnable/Callable 中手动处理,或用 UncaughtExceptionHandler。
  Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
      System.out.println("线程异常:" + e);
  });
  • 性能影响:异常是昂贵的(栈追踪开销),避免用异常控制流程(如用 if 代替)。

6. 最佳实践(生产级建议)

  1. 捕获具体异常:优先捕获子类(如 IOException 而非 Exception),避免隐藏 bug。
  2. 不要吞异常:catch 块至少记录日志(如 log.error(e)),别空 catch。
  3. finally 释放资源:或优先 try-with-resources。
  4. Checked 异常 vs Unchecked
  • Checked:用于可恢复场景(如用户输入)。
  • Unchecked:用于编程错误(如参数校验)。
  1. 日志记录:用 SLF4J/Log4j 记录栈追踪:log.error("错误", e);
  2. 全局异常处理(Spring 项目):用 @ControllerAdvice + @ExceptionHandler。
  3. 避免过度捕获:让异常向上传播到合适层级处理。
  4. 测试异常:用 JUnit @Test(expected = Exception.class) 或 Assertions.assertThrows()。
  5. 2025+ 趋势:结合 Virtual Thread(Project Loom),异常在并发中更易追踪。

常见问题排查

  • StackOverflowError:递归太深或无限循环。
  • OutOfMemoryError:内存泄漏,监控 JVM(用 jvisualvm)。
  • NullPointerException:最常见,防御式编程(Optional 或 null 检查)。

掌握这些,你能写出更可靠的 Java 代码!如果想深入某个部分(如 Spring 异常处理或更多示例),告诉我,我们继续。

文章已创建 4138

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部