受检异常与非受检异常如何设计?finally 的规则是什么?

结论先行:受检异常(checked)继承 Exception 但不属于 RuntimeException,编译器强制捕获或声明 throws,代表可预期、应恢复的错误(如 IOException);非受检异常(unchecked)继承 RuntimeException 或 Error,不强制处理,代表程序缺陷或不可恢复的问题(如 NPE、OOM)。finally 块在正常结束、return 前、异常抛出时都会执行,唯一例外是 JVM 退出或线程被强杀。

设计建议

场景推荐类型示例
外部、可恢复的错误受检异常文件不存在、网络超时
参数/状态错误非受检异常IllegalArgumentException、NullPointerException
框架与底层逻辑错误非受检异常IllegalStateException

finally 三条铁律

  1. finally 里不要 return:它会覆盖 try/catch 的返回值,甚至吞掉异常。
  2. try 或 catch 中先 return 时,finally 仍会先执行完毕才真正返回。
  3. finally 中若抛出新异常,会替换原来的异常(资源关闭场景常用 try-with-resources 规避)。
import java.io.*;

public class FinallyDemo {
    static int test() {
        try {
            return 1;      // 先执行 finally 再返回
        } finally {
            System.out.println("finally 一定执行");
            // 若这里写 return 2,会覆盖上面的 1 —— 反模式
        }
    }

    static void read() throws IOException { // 受检异常必须声明或捕获
        InputStream in = new FileInputStream("x.txt");
        try {
            in.read();
        } finally {
            in.close(); // 此时 close 抛错会掩盖原异常,JDK7+ 应改用 try-with-resources
        }
    }
}

常见追问 / 记忆点

  • 记忆点:受检 = 该处理的外部错误,非受检 = 有 Bug 或不可恢复;finally 别 return、别抛新异常。
  • 追问:Spring 声明式事务默认只对 RuntimeException 与 Error 回滚,受检异常默认不触发回滚。
  • 追问:finally 中 System.exit、JVM Crash、守护线程中断等极少数场景可能不执行。
笔记加载中…