Java-异常处理机制-try-catch

Java 异常处理机制(try-catch-finally-throw-throws)超级详细讲解

这是 Java 基础里最实用、最常被面试官深挖的模块之一。
很多人在实际开发中只写 try-catch,但其实没搞懂异常的传播机制、异常链、资源释放、性能影响等,导致代码写得既不优雅也不健壮。

1. Java 异常体系全景图(2026 年仍然不变)

Throwable(顶级父类)
├── Error(严重错误,通常不可恢复)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   ├── VirtualMachineError
│   └── ...
└── Exception(普通异常,可捕获、可恢复)
    ├── RuntimeException(运行时异常/非检查异常)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── ClassCastException
    │   ├── ArithmeticException
    │   ├── IllegalArgumentException
    │   └── ...
    └── 其他 checked Exception(检查异常,必须处理)
        ├── IOException
        │   ├── FileNotFoundException
        │   └── ...
        ├── SQLException
        ├── ClassNotFoundException
        └── ...

最重要分类

类型是否必须处理(编译期检查)代表异常什么时候抛出典型处理方式
ErrorOutOfMemoryErrorJVM 级别严重问题基本不处理
RuntimeException否(unchecked)NullPointerException 等程序逻辑错误应该避免而不是捕获
checked Exception是(必须处理)IOException、SQLException 等外部环境问题(文件、网络、数据库)try-catch 或 throws

2. try-catch-finally 最完整写法(记住这几种模式)

// 模式1:最常见(捕获 + 处理)
try {
    // 可能抛出异常的代码
    FileInputStream fis = new FileInputStream("a.txt");
} catch (FileNotFoundException e) {
    // 精确捕获具体异常
    System.out.println("文件不存在:" + e.getMessage());
    // 可以记录日志、返回错误码、给用户友好提示
} catch (IOException e) {   // 可以写多个 catch
    System.out.println("IO 异常:" + e.getMessage());
} catch (Exception e) {     // 兜底(一般放最后)
    e.printStackTrace();   // 开发阶段常用,上线慎用
} finally {
    // 无论是否异常都会执行(常用于资源释放)
    System.out.println("finally 一定执行");
}

模式2:带资源管理的写法(Java 7+ 强烈推荐)

// 自动关闭资源(AutoCloseable 接口)
try (FileInputStream fis = new FileInputStream("a.txt");
     BufferedInputStream bis = new BufferedInputStream(fis)) {
    // 使用 fis 和 bis
    // 不需要手动 close()
} catch (IOException e) {
    // 处理异常
}

模式3:只抛不捕获(throws)

public void readFile() throws IOException, FileNotFoundException {
    // 方法签名声明可能抛出的 checked 异常
    new FileInputStream("不存在.txt");
}

3. 异常处理最佳实践(面试 + 真实开发必背)

  1. 不要 catch Throwable / Exception 兜底所有异常
  • 错误的写法:catch (Exception e) 然后啥也不干
  • 后果:隐藏了真正的 bug
  1. 捕获越具体越好(从子类 → 父类)
   catch (FileNotFoundException e) { ... }  // 先
   catch (IOException e) { ... }            // 后
  1. 不要在 finally 里再抛异常或 return
  • finally 里的 return 会覆盖 try/catch 里的 return
  • finally 抛异常会覆盖前面的异常(异常链丢失)
  1. 异常链不要断(推荐做法)
   catch (IOException e) {
       throw new RuntimeException("读取配置文件失败", e);  // 保留原始异常
   }
  1. 自定义异常(企业级项目常用)
   public class BusinessException extends RuntimeException {
       private final int code;

       public BusinessException(int code, String message) {
           super(message);
           this.code = code;
       }

       public int getCode() { return code; }
   }
  1. 性能考虑:异常不应该作为正常流程控制
  • 错的用法:try { Integer.parseInt(str) } catch (NumberFormatException e) { return 0; }
  • 正确:先用正则或 Character.isDigit 判断

4. 经典面试题 & 陷阱题(高频)

题1:下面代码最终输出什么?

public static int test() {
    int i = 10;
    try {
        return i;
    } finally {
        i = 20;
    }
}

答案:10
finally 里的修改不影响已经准备返回的值(基本类型值传递)

题2:有 return 的情况下 finally 还会执行吗?

答案:,但 finally 先执行,return 的值已经“定格”

题3

try {
    return 1;
} catch (Exception e) {
    return 2;
} finally {
    return 3;
}

答案:3(finally 的 return 会覆盖前面所有)

题4:throw 和 throws 的区别?

  • throw:方法体内主动抛异常(手动)
  • throws:方法签名声明可能抛出的异常(告诉调用者)

题5:异常被 finally 覆盖的经典案例

try {
    throw new Exception("A");
} finally {
    throw new RuntimeException("B");
}

最终抛出的是 B,A 被“吞掉”了(非常危险)

5. 推荐的现代异常处理风格(2025-2026 主流)

// 推荐写法
public Result<User> getUserById(Long id) {
    if (id == null || id <= 0) {
        return Result.error(400, "ID 非法");
    }

    try {
        User user = userService.findById(id);
        if (user == null) {
            return Result.error(404, "用户不存在");
        }
        return Result.ok(user);
    } catch (DataAccessException e) {
        log.error("数据库异常", e);
        return Result.error(500, "系统繁忙,请稍后重试");
    } catch (Exception e) {
        log.error("未知异常", e);
        return Result.error(500, "系统错误");
    }
}
  • 业务异常 → 用自定义 BusinessException + 状态码
  • 系统异常 → 统一兜底 + 日志 + 友好提示
  • 避免层层 throws checked exception(改成 RuntimeException 包装)

重阳,现在你对异常处理最困惑的是哪一块?

  • finally 与 return 的执行顺序细节?
  • 异常链如何完整保留?
  • 自定义异常 + 全局异常处理(@ControllerAdvice)?
  • try-with-resources 的底层原理?
  • 生产环境日志怎么记录异常?

告诉我,我们继续深挖~

文章已创建 4357

发表回复

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

相关文章

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

返回顶部