OutOfMemoryError 等 Error 是 JVM 系统级崩溃信号,不应 try-catch,而应排查内存配置、GC 日志或堆 dump;IOException 等已检查异常必须处理,NullPointerException 等未检查异常反映代码逻辑缺陷。

看到 OutOfMemoryError 别急着 try-catch
Java 中所有异常的顶层父类是 Throwable,它只派生出两个子类:Error 和 Exception。关键区别就一句话:程序该不该、能不能处理它。Error 是 JVM 自己扛不住的系统级崩溃信号,比如 OutOfMemoryError、StackOverflowError、NoClassDefFoundError——它们不是你代码写错了,而是 JVM 运行环境崩了。此时捕获并“吞掉”它,只会让程序带病运行,状态不可知,后续行为更危险。
- 不要在业务逻辑里写
catch (Error e),哪怕加了日志也毫无意义 - 如果真想兜底(如监控告警),只能用
Thread.setDefaultUncaughtExceptionHandler捕获线程级未处理的Throwable,但不建议恢复执行 -
Error是编译器完全不管的——你漏写 catch 不会报错,但这恰恰说明它不该出现在你的异常处理链里
IOException 必须处理,NullPointerException 可以(但最好别发生)
Exception 分两类:编译器强制你面对的 已检查异常(Checked Exception),和编译期放过的 未检查异常(Unchecked Exception)。前者代表外部不确定性(文件可能被删、网络可能断开),后者基本等于你代码没写严谨(对象没判空、数组越界、类型强转失败)。
-
IOException、SQLException属于已检查异常:不try-catch或不声明throws,编译直接失败 -
NullPointerException、ArrayIndexOutOfBoundsException是RuntimeException子类,属于未检查异常:编译通过,但运行时炸了就是你逻辑漏洞 - 别为了省事把已检查异常全包进
catch (Exception e)然后e.printStackTrace()——这等于放弃错误语义,掩盖真实问题
为什么 throw new Error("xxx") 几乎从不出现?
你几乎不会自己 new Error(...) 抛出错误,因为 Error 的设计意图不是给应用层用的。它是 JVM 在底层资源耗尽或结构损坏时自动抛出的“终止信号”。你自己造一个 Error,既不符合语义,也无法触发 JVM 的保护机制(比如 OOM 时的 GC 尝试或堆 dump)。
- 自定义错误场景,一律用继承
Exception或RuntimeException的子类(如ValidationException、BusinessRuleViolationException) - 若真要表示“这地方绝不该走到”,用
throw new AssertionError("unreachable")更合适——它也是Error子类,但专用于断言失败,且只在-ea启动参数下生效,开发阶段可查,生产默认关闭 - 强行
throw new OutOfMemoryError()不会触发内存回收,只是立刻杀死当前线程,毫无实际价值
排查时看堆栈最上面一行是 Error 还是 Exception
线上日志或本地调试遇到崩溃,第一眼盯住堆栈最顶端那行:java.lang.OutOfMemoryError 还是 java.io.FileNotFoundException?这个判断直接决定处理路径:
立即学习“Java免费学习笔记(深入)”;
- 如果是
Error开头:立刻查 JVM 参数(-Xmx是否过小)、GC 日志、是否有内存泄漏(用jmap -histo或 MAT 分析 heap dump) - 如果是
Exception开头:先定位抛出位置,看是外部依赖(DB/HTTP/File)不稳定,还是输入校验缺失,或是并发竞争导致状态不一致 - 特别注意
NoClassDefFoundError:它名字像Error,但常因类加载器隔离、依赖版本冲突引起——这其实是可诊断、可修复的“配置问题”,不是真正的系统崩溃
Error 和 Exception,而是在 catch (Exception e) 里,你是否还知道它原本是 SQLException 还是 TimeoutException;在 OutOfMemoryError 日志里,你能否从 java_pid*.hprof 文件里一眼看出哪个对象占了 80% 堆内存。分类只是起点,细节才决定能不能修。








