应捕获Exception而非Error:前者可恢复(如IO异常),后者表JVM崩溃(如OOM),捕获Error会导致数据错乱;Checked异常强制处理,Unchecked异常暴露逻辑缺陷;自定义异常禁继承Error。

Exception 是程序能干预的意外,Error 是 JVM 自己扛不住了
直接说结论:你该捕获 Exception,但绝不该捕获 Error。因为前者代表“这事我能救”,比如文件没找到、网络超时、数组越界;后者代表“地基塌了”,比如 OutOfMemoryError、StackOverflowError、NoClassDefFoundError——JVM 已无法保证后续行为正确,强行 catch 后继续执行,大概率引发数据错乱或静默失败。
为什么不能写 catch(Error e) 来“兜底”
这不是语法错误,而是设计反模式。真实项目里见过有人在全局异常处理器里加 catch(Throwable t),本意是“防崩”,结果把 OutOfMemoryError 也吞了,日志只打一行“捕获到 Throwable”,内存持续泄漏却毫无感知。
-
Error不是设计给业务代码处理的,它不提供恢复语义,JVM 在抛出后通常已进入不可信状态 - 捕获
Error后调用e.printStackTrace()或记录日志,对排障几乎无用——真正要的是 JVM 堆转储(heap dump)、GC 日志、启动参数分析 - 若真想监控,应通过
Thread.setUncaughtExceptionHandler捕获未处理的Error,仅用于告警和进程优雅退出,而非“继续跑业务”
Exception 分两类,编译器管不管,决定了你能不能“偷懒”
Java 强制你面对一部分异常,不是为了为难你,而是提醒:“这事儿外部环境说了算,你得提前想好 Plan B”。
-
检查型异常(Checked Exception):如
IOException、SQLException。方法签名里写了throws,你就必须try-catch或向上声明——否则编译失败 -
非检查型异常(Unchecked Exception):即
RuntimeException及其子类,如NullPointerException、IllegalArgumentException。编译器放行,但不等于可以忽略;它们往往暴露逻辑缺陷,比如没校验入参就调用.length()
注意:自定义异常务必继承 Exception(需检查)或 RuntimeException(不需检查),永远不要继承 Error——那等于告诉所有人“我这个异常是 JVM 级灾难”。
立即学习“Java免费学习笔记(深入)”;
容易被忽略的实战细节
很多问题不是出在“会不会写 try-catch”,而是出在边界认知模糊:
-
ClassNotFoundException是Exception,但运行时几乎无法恢复(类路径缺失),实际应归为“启动失败”,不该靠 catch 重试,而应检查打包、依赖、ClassLoader 层级 -
AssertionError是Error子类,但它由assert触发,属于开发期断言——上线时通常禁用,捕获它毫无意义 - Spring 等框架会把底层
Error包装成RuntimeException抛出(如BeanCreationException),这时你看到的是Exception,但根因可能是NoClassDefFoundError,得看getCause()
真正关键的,从来不是“怎么 catch”,而是“这个异常到底意味着什么系统状态”——Exception 要问“怎么恢复”,Error 要问“为什么发生”。










