原文来自于:zha-ge.cn/java/24
你真的知道 Java 里的 Exception 和 Error 有啥不同吗?
一个看似简单的面试题
那天和小李聊天,他刚面试回来,一脸郁闷:"面试官问我 Exception 和 Error 的区别,我说了半天继承关系,结果他说答得太浅显了。这不都是异常吗,有啥区别?"
我笑了笑,想起自己当年也是这么想的。直到那次生产环境的惨案...
那个让我印象深刻的周五下午
记得那是个普通的周五,我正准备下班,突然运维小哥慌张地跑过来:"系统挂了!用户都在投诉!"
打开监控一看,服务器内存爆了,日志里全是这样的信息:
java
java.lang.OutOfMemoryError: Java heap space
at java.util.ArrayList.grow(ArrayList.java:267)
at com.example.service.DataProcessor.processLargeData(DataProcessor.java:45)
我当时第一反应是用 try-catch 包住这段代码,结果发现根本捕获不到!
java
try {
// 处理大量数据
List<String> hugeList = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
hugeList.add("data_" + i);
}
} catch (Exception e) {
// 这里永远不会执行!
logger.error("处理失败", e);
}
踩坑瞬间
那一刻我才意识到,OutOfMemoryError 压根不是 Exception,而是 Error!
查了源码才发现,Java 的异常体系是这样的:
- Throwable (老祖宗)
- Error :系统级错误,程序无法处理
- OutOfMemoryError(内存不足)
- StackOverflowError(栈溢出)
- NoClassDefFoundError(找不到类)
- Exception :程序级异常,可以被处理
- RuntimeException(运行时异常)
- 检查异常(编译时必须处理)
- Error :系统级错误,程序无法处理
真正的区别在哪里?
经过那次事故,我总结了三个本质区别:
1. 处理思路完全不同
Exception 是"可以商量的":
java
try {
int result = 10 / 0; // ArithmeticException
} catch (ArithmeticException e) {
// 可以优雅处理,比如返回默认值
return -1;
}
Error 是"不讲道理的",你只能接受现实,最多记录一下:
java
// Error 一般不建议捕获,但如果非要捕获:
try {
recursiveMethod(); // 可能导致 StackOverflowError
} catch (Error e) {
// 只能记录,然后程序该挂还是会挂
logger.fatal("系统崩溃", e);
System.exit(1);
}
2. 产生原因天差地别
类型 | 产生原因 | 典型场景 |
---|---|---|
Exception | 业务逻辑问题、外部环境变化 | 文件不存在、网络超时、参数错误 |
Error | JVM 或系统资源问题 | 内存耗尽、栈空间不足、类加载失败 |
3. 影响范围不一样
- Exception:通常影响单个操作或请求
- Error:往往影响整个应用甚至 JVM
经验启示
那次生产事故后,我养成了几个习惯:
- 监控内存使用:定期检查堆内存、栈深度
- 区别对待异常:Exception 用于业务处理,Error 用于系统预警
- 谨慎捕获 Error:除非做日志记录,否则不要轻易 catch Error
最重要的是,理解了这个区别后,面对异常时的思考方式完全变了:
- 遇到 Exception:这个问题我能解决吗?怎么优雅处理?
- 遇到 Error:系统哪里出问题了?需要扩容还是优化?
写在最后
现在再有人问我 Exception 和 Error 的区别,我不会只说继承关系了。它们就像感冒和骨折的区别:
- Exception 像感冒,吃点药、休息一下就好了
- Error 像骨折,得上医院,不处理会要命
下次遇到这个问题,你知道怎么回答了吗?