在编程的世界里,墨菲定律总是生效的:“如果事情有变坏的可能,不管这种可能性有多小,它总会发生。” 在 Java 中,异常(Exception) 就是处理这些“意外”的基石。

一、 异常的家谱:知己知彼

理解异常,首先要看懂它的家族结构。所有的异常都继承自 java.lang.Throwable 类。

1. Error(错误)

这是 JVM 层面无法挽回的严重问题,比如 OutOfMemoryError(内存溢出)或 StackOverflowError(栈溢出)。

建议: 你的代码不应该尝试捕获(catch)这些错误。

2. Exception(异常)

这是程序本身可以处理的情况,分为两类:

• 受检异常 (Checked Exception):编译器强制要求你处理(如 IOException)。它提醒你:这里很可能出问题,请提前想好退路。

• 非受检异常 (Unchecked/Runtime Exception):如 NullPointerException。这些通常是代码逻辑漏洞,编译器不会强制你捕获,但它们会直接导致程序崩溃。

二、 异常处理的“三板斧”

1. 传统的 Try-Catch-Finally

这是最基础的防御手段。

try {
    // 可能发生意外的代码
    File file = new File("config.txt");
    FileInputStream fis = new FileInputStream(file);
} catch (FileNotFoundException e) {
    // 应对方案
    log.error("找不到配置文件!", e);
} finally {
    // 无论如何都会执行(通常用于关闭流)
    System.out.println("清理资源...");
}

2. 优雅的 Try-with-resources

在处理 IO 流、数据库连接时,手动在 finally 里关资源既繁琐又容易出错。Java 7 引入了这一语法糖:

try (FileInputStream fis = new FileInputStream("test.txt")) {
    // 使用资源
} catch (IOException e) {
    // 自动关闭 fis,无需 finally
}

3. Throws 甩锅大法

如果你不想在当前方法处理异常,可以将其向上抛出,交给调用者决定:

public void readFile() throws IOException { ... }

三、 实战中的避坑指南(干货)

1. 拒绝“吃掉”异常

最忌讳的代码:

try {
    // do something
} catch (Exception e) {} // 异常被静默处理了,排查问题时你会想哭

正确做法: 至少要记录日志,或者将其包装成业务异常抛出。

2. 捕获要精确

不要总是 catch (Exception e)。尽量捕获具体的子类,这样你能根据不同的错误类型(如数据库连接失败 vs 数据格式错误)采取不同的补救措施。

3. 不要用异常做流程控制

异常的设计初衷是处理“非正常情况”。如果你用 try-catch 来代替 if-else 做业务逻辑判断,会导致代码性能大幅下降且难以维护。

四、 自定义业务异常

在大型项目中,我们通常会定义自己的异常类,以便统一错误码和返回格式:

public class BusinessException extends RuntimeException {
    private int errorCode;
    public BusinessException(String message, int errorCode) {
        super(message);
        this.errorCode = errorCode;
    }
}

结语

异常处理不仅仅是为了让程序不崩,更是为了让程序在面对错误时能优雅地降级。一份好的代码,不仅在于其正常逻辑的精妙,更在于其应对意外时的稳健。

你是如何处理项目中的全局异常的?

如果你对 Spring Boot 中的 @RestControllerAdvice 全局异常处理器感兴趣,欢迎在评论区留言,我们下期详细拆解!

您可以直接将上述内容发布。如果需要我为您补充具体的 Spring Boot 全局异常拦截代码示例,请随时告诉我。

 

 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐