17370845950

在Java里Error和Exception如何区分_Java错误模型说明
OutOfMemoryError 等 Error 是 JVM 系统级崩溃信号,不应 try-catch,而应排查内存配置、GC 日志或堆 dump;IOException 等已检查异常必须处理,NullPointerException 等未检查异常反映代码逻辑缺陷。

看到 OutOfMemoryError 别急着 try-catch

Java 中所有异常的顶层父类是 Throwable,它只派生出两个子类:ErrorException。关键区别就一句话:程序该不该、能不能处理它Error 是 JVM 自己扛不住的系统级崩溃信号,比如 OutOfMemoryErrorStackOverflowErrorNoClassDefFoundError——它们不是你代码写错了,而是 JVM 运行环境崩了。此时捕获并“吞掉”它,只会让程序带病运行,状态不可知,后续行为更危险。

  • 不要在业务逻辑里写 catch (Error e),哪怕加了日志也毫无意义
  • 如果真想兜底(如监控告警),只能用 Thread.setDefaultUncaughtExceptionHandler 捕获线程级未处理的 Throwable,但不建议恢复执行
  • Error 是编译器完全不管的——你漏写 catch 不会报错,但这恰恰说明它不该出现在你的异常处理链里

IOException 必须处理,NullPointerException 可以(但最好别发生)

Exception 分两类:编译器强制你面对的 已检查异常(Checked Exception),和编译期放过的 未检查异常(Unchecked Exception)。前者代表外部不确定性(文件可能被删、网络可能断开),后者基本等于你代码没写严谨(对象没判空、数组越界、类型强转失败)。

  • IOExceptionSQLException 属于已检查异常:不 try-catch 或不声明 throws,编译直接失败
  • NullPointerExceptionArrayIndexOutOfBoundsExceptionRuntimeException 子类,属于未检查异常:编译通过,但运行时炸了就是你逻辑漏洞
  • 别为了省事把已检查异常全包进 catch (Exception e) 然后 e.printStackTrace() ——这等于放弃错误语义,掩盖真实问题

为什么 throw new Error("xxx") 几乎从不出现?

你几乎不会自己 new Error(...) 抛出错误,因为 Error 的设计意图不是给应用层用的。它是 JVM 在底层资源耗尽或结构损坏时自动抛出的“终止信号”。你自己造一个 Error,既不符合语义,也无法触发 JVM 的保护机制(比如 OOM 时的 GC 尝试或堆 dump)。

  • 自定义错误场景,一律用继承 ExceptionRuntimeException 的子类(如 ValidationExceptionBusinessRuleViolationException
  • 若真要表示“这地方绝不该走到”,用 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,但常因类加载器隔离、依赖版本冲突引起——这其实是可诊断、可修复的“配置问题”,不是真正的系统崩溃
真正难的不是分清 ErrorException,而是在 catch (Exception e) 里,你是否还知道它原本是 SQLException 还是 TimeoutException;在 OutOfMemoryError 日志里,你能否从 java_pid*.hprof 文件里一眼看出哪个对象占了 80% 堆内存。分类只是起点,细节才决定能不能修。