杰瑞科技汇

Java中如何catch多个异常?

多个 catch 块 (Multiple Catch Blocks)

这是最传统、最直观的方式,为每个需要捕获的异常类型编写一个独立的 catch 块。

Java中如何catch多个异常?-图1
(图片来源网络,侵删)

语法结构

try {
    // 可能抛出异常的代码
    int result = 10 / 0;
    String str = null;
    int length = str.length();
} catch (ArithmeticException e) {
    // 专门处理算术异常,如除以零
    System.err.println("捕获到算术异常: " + e.getMessage());
} catch (NullPointerException e) {
    // 专门处理空指针异常
    System.err.println("捕获到空指针异常: " + e.getMessage());
} catch (Exception e) {
    // 这是一个通用的异常捕获,通常放在最后,用于捕获未预料到的其他异常
    System.err.println("捕获到未知异常: " + e.getMessage());
}

工作原理

JVM 会按照 catch 块的书写顺序,从上到下依次检查抛出的异常对象是否与 catch 块中的异常类型匹配,一旦找到第一个匹配的 catch 块,就会执行该块中的代码,然后跳出整个 try-catch 结构。

重要提示:更具体的异常类型应该放在更通用的异常类型之前,如果将 catch (Exception e) 放在最前面,它将捕获所有类型的异常,导致后面的 catch 块永远无法被执行,这在编译时不会报错,但逻辑上是错误的。

// 错误的顺序!
catch (Exception e) { ... } // 这个会捕获所有异常
catch (ArithmeticException e) { ... } // 这行代码永远不会执行到

优点

  1. 清晰明了:每个异常都有自己独立的处理逻辑,代码意图非常清晰,易于阅读和维护。
  2. 处理方式灵活:可以为不同类型的异常提供完全不同的恢复策略或错误信息。
  3. 调试方便:在调试时,可以轻松定位到是哪个特定的异常分支被触发了。

缺点

  1. 代码冗余:如果多个异常的处理逻辑完全相同,就会导致重复的代码。

单个 catch 块捕获多个异常 (Java 7+)

从 Java 7 开始,引入了一种更简洁的语法,允许在一个 catch 块中捕获多个不同类型的异常,这些异常类型使用竖线 分隔。

语法结构

try {
    // 可能抛出异常的代码
    int result = 10 / 0;
    String str = null;
    int length = str.length();
} catch (ArithmeticException | NullPointerException e) {
    // 一个 catch 块处理两种异常
    System.err.println("捕获到算术异常或空指针异常: " + e.getMessage());
    // 注意:这里的 e 变量是 final 的,不能被重新赋值
    // e = new Exception(); // 编译错误!
}

工作原理

这种方式在底层会被编译器转换成多个 catch 块,编译器会为 分隔的每个异常类型都生成一个隐式的 catch 块,并将你的代码块内容复制到每个隐式 catch 块中。

Java中如何catch多个异常?-图2
(图片来源网络,侵删)
// 编译器内部大致会转换成这样
try {
    // ...
} catch (ArithmeticException e) {
    System.err.println("捕获到算术异常或空指针异常: " + e.getMessage());
} catch (NullPointerException e) {
    System.err.println("捕获到算术异常或空指针异常: " + e.getMessage());
}

优点

  1. 代码简洁:当多个异常的处理逻辑相同时,可以大大减少代码量,避免重复。
  2. DRY (Don't Repeat Yourself):遵循了不重复代码的原则。

缺点

  1. 处理逻辑统一:所有被捕获的异常都必须执行完全相同的处理逻辑,如果需要对不同异常进行不同处理,则无法使用此方式。
  2. 异常变量限制:在 catch 块中,异常变量 e 被隐式声明为 final,不能被重新赋值。
  3. 可能掩盖细节:将不同来源的异常用同一种方式处理,可能会掩盖某些异常特有的重要信息。

如何选择?最佳实践

选择哪种方式取决于你的具体需求。

使用 catch (Exception1 | Exception2 e) 当:

  • 多个异常的处理逻辑完全相同,这是使用此语法最主要的原因。
  • 你希望代码更简洁,减少冗余。

示例: 假设你正在关闭一个 AutoCloseable 资源(如 FileInputStream),close() 方法可能抛出 IOException,你的业务逻辑也可能抛出 SQLException,如果对这两个异常的处理都是简单地打印日志并记录,那么就可以合并。

try (FileInputStream fis = new FileInputStream("test.txt")) {
    // 业务逻辑,可能抛出 SQLException
    // ...
} catch (IOException | SQLException e) {
    // 对两种异常执行相同的操作:记录日志
    logger.error("操作失败", e);
}

使用多个 catch 块 当:

  • 不同异常需要不同的处理逻辑,这是最常见的情况,也是推荐的做法。
  • 需要针对特定异常类型提供更精确的错误信息或恢复机制。
  • 你需要根据异常类型执行不同的代码分支。

示例: 用户登录时,可能因为密码错误(BadCredentialsException)或账户被锁定(AccountLockedException)而失败,这两种情况应该给用户不同的提示。

try {
    user.login(username, password);
} catch (BadCredentialsException e) {
    // 提示用户密码错误
    showErrorMessage("用户名或密码错误,请重试。");
} catch (AccountLockedException e) {
    // 提示用户账户被锁定
    showErrorMessage("账户已被锁定,请联系管理员。");
} catch (LoginException e) {
    // 其他登录异常
    showErrorMessage("登录失败,请稍后再试。");
}

| 特性 | 多个 catch 块 | 单个 catch 块 () | | :--- | :--- | :--- | | 适用场景 | 异常处理逻辑不同 | 异常处理逻辑相同 | | 代码清晰度 | 非常高,逻辑分离清晰 | 较高,但可能掩盖差异 | | 代码简洁性 | 可能冗余 | 非常简洁,避免重复 | | 灵活性 | ,可为每个异常定制处理 | ,必须统一处理 | | Java 版本 | Java 1.0+ | Java 7+ |

Java中如何catch多个异常?-图3
(图片来源网络,侵删)

核心建议优先使用多个 catch 块,因为它能让代码的意图更清晰。 只有当明确知道多个异常的处理逻辑完全相同时,才考虑使用 catch (Exception1 | Exception2 e) 来简化代码。

分享:
扫描分享到社交APP
上一篇
下一篇