Throwable 是所有异常的唯一源头,是携带类型、堆栈和嵌套原因的“错误信封”;Error 表示 JVM 无法恢复的严重错误,不应捕获;Exception 分为需强制处理的编译时异常(如 IOException)和反映逻辑错误的运行时异常(如 NullPointerException)。

Java异常处理机制,本质是一套“提前约定错误信号、分层响应问题”的运行时协作规则——不是让程序不报错,而是让错误可识别、可定位、可分流。
什么是 Throwable?它是所有异常的唯一源头
Java里没有“异常”这个独立概念,只有 Throwable 类及其子类。它不是工具类,而是一个携带上下文信息的“错误信封”:包含异常类型、堆栈快照(printStackTrace() 输出的内容)、可选的嵌套原因(getCause())。
-
Error是 JVM 自己扛不住的崩溃信号(如OutOfMemoryError、StackOverflowError),程序不该 catch,更不该 try-catch 后继续跑 -
Exception才是程序员要打交道的“可干预异常”,再细分为两类: - 编译时异常(
Checked Exception):比如IOException、SQLException,编译器强制你处理——要么try-catch,要么方法签名加throws - 运行时异常(
RuntimeException及其子类):比如NullPointerException、ArrayIndexOutOfBoundsException,编译器不管,但它们几乎全是代码逻辑漏洞的直接暴露
为什么 try-catch-finally 不是万能解药?
很多人把 try-catch 当成“防崩补丁”,结果写出满屏 catch (Exception e) { e.printStackTrace(); } ——这等于把报警器拆了,还堵住警铃。
- 捕获太宽泛(如
catch (Exception e))会吞掉本该暴露的RuntimeException,掩盖真实 bug -
finally里没判空就调close(),可能引发二次异常,覆盖原始异常(原始异常反而丢失) - 资源未用
try-with-resources自动管理,finally里手动 close 容易漏写或出错 - 在
catch块里只打印堆栈却不记录日志、不通知监控、不返回有意义的状态,等于没处理
throws 和 throw 的分工很明确:一个甩锅,一个点火
throws 出现在方法声明末尾,是向调用方“声明契约”:我可能抛出这些异常,请你接住;throw 出现在方法体内,是主动触发一个异常对象,常用于校验失败时提前中断。
立即学习“Java免费学习笔记(深入)”;
- 不要在私有工具方法里滥用
throws Exception——它破坏封装,让调用方被迫处理所有未知异常 - 自定义异常应继承
Exception(需检查)或RuntimeException(不需检查),取决于是否希望调用方必须响应 - 用
throw new IllegalArgumentException("timeout must be > 0")比if (timeout 更清晰——错误语义直白,调用链上更容易追溯 - 抛出异常前,优先考虑是否真需要中断流程;有些场景用 Optional 或返回错误码更轻量
常见误区:把“不报错”当成“处理好了”
很多代码看似通过了编译、跑通了测试,却在生产环境突然失效——因为异常被静默吞掉、被错误重试、或被当成普通流程分支。
-
catch块里只写log.info(...)而非log.error(...),导致错误被淹没在海量 info 日志中 - 对网络请求反复
try-catch-retry却不限制重试次数和退避策略,可能放大下游压力 - 在 finally 中执行耗时操作(如远程日志上报),拖慢主流程,甚至引发超时
- 把
InterruptedException简单 catch 后忽略,导致线程中断状态丢失,影响上层调度逻辑
异常处理真正的难点不在语法,而在判断:这个异常是该立刻终止、降级响应、重试、还是记录后继续?这需要结合业务语义,而不是套模板。










