受检异常必须处理:不try-catch就得throws,否则编译报错;核心是权衡责任归属——能本地恢复(如fallback、重试、关流)则catch,否则throws交由上层决策。

受检异常必须处理,不选 throw 就得 try-catch
Java 编译器强制你面对 IOException、SQLException 这类受检异常——既不能忽略,也不能只靠运行时兜底。不写 try-catch,就得在方法签名加 throws,否则编译直接报错:Unhandled exception type XXXException。
这不是风格选择,是编译规则。所谓“选择”,其实是权衡责任归属:当前方法有没有能力、有没有职责去恢复或解释这个异常?
什么时候该用 try-catch 而不是 throws
核心判断标准:能否在当前上下文做出有意义的响应。比如读配置文件失败,你可以在 catch 里加载默认值;连接数据库失败,可以重试一次或降级到缓存。
- 能提供 fallback 行为(如返回默认值、记录日志后继续执行)
- 异常原因可控且局部可修复(如临时文件被占用,改用临时目录)
- 调用方不关心细节,只需要结果(比如 UI 层统一弹“操作失败”,不必暴露
FileNotFoundException) - 需要清理资源(如关闭
FileInputStream),且用 try-with-resources 更安全
反例:DAO 方法里 catch 住 SQLException 又原样封装成新异常再 throw,等于白写——不如直接 throws SQLException,让上层决定怎么处理。
立即学习“Java免费学习笔记(深入)”;
什么时候该用 throws 向上传递
当异常本质属于调用方该决策的问题,当前方法只是“中转站”。典型场景是工具类、底层 IO 或数据访问代码。
- 方法语义本身不包含错误恢复逻辑(如
FileUtils.readFileToString(File)) - 调用方类型多样(可能是 Web 控制器、批处理任务、定时脚本),错误处理策略完全不同
- 当前方法没有足够上下文判断“算不算失败”(比如读一个非关键配置,空了也能跑,但你不知道)
- 强行 try-catch 只是打日志+rethrow,没增加任何业务价值
注意:throws 不代表甩锅,而是明确契约。调用方看到方法声明了 throws IOException,就必须正视它——要么处理,要么继续抛。
常见踩坑点:吞异常、乱包装、忽略 close
最典型的错误不是“选错”,而是“假装处理”:
- catch 块里只写
e.printStackTrace(),然后静默返回,导致上游完全收不到失败信号 - 把
IOException包装成RuntimeException(比如new RuntimeException(e))又不声明,绕过编译检查,破坏调用方预期 - 手动
try-catch-finally关流,却在 catch 里 return 或 throw,导致 finally 中的close()被跳过 - 用 try-with-resources 时,把多个资源写在同一行(
try (var a = ..., b = ...)),一旦 a 构造失败,b 根本不会初始化,但很多人误以为会自动 close
真正难的从来不是语法,而是判断哪一层该知道磁盘满了、哪一层该知道用户密码错了。异常链条越长,越容易在中间某层把语义弄丢。










