Java 实战 -Error和Exception有什么区别?

Java 实战角度:Error 和 Exception 到底有什么区别?

这是一个 Java 面试 + 实际开发中出现频率极高的问题,但很多开发者回答得比较表面。下面从实际使用视角把两者的本质区别、内存模型中的表现、使用场景、应该怎么处理,讲得更清楚一些。

一、核心区别一句话总结

维度ErrorException
含义严重系统错误,程序通常无法恢复可预见、可处理的异常情况
是否必须处理不需要(也不建议)捕获大部分建议捕获并处理(尤其是 checked)
是否可恢复基本不可恢复(JVM 本身出问题)大部分可以恢复(业务逻辑问题)
典型场景OutOfMemoryError、StackOverflowError、VirtualMachineErrorNullPointerException、IOException、SQLException、IllegalArgumentException
继承关系继承自 java.lang.Error继承自 java.lang.Exception
是否 checked全部 unchecked分 checked(必须处理)和 unchecked

二、类继承结构图(最直观)

Throwable(顶级父类)
├── Error(严重错误)
│   ├── VirtualMachineError
│   │   ├── OutOfMemoryError
│   │   ├── StackOverflowError
│   │   └── InternalError
│   ├── LinkageError
│   └── ...其他系统级错误
└── Exception(普通异常)
    ├── RuntimeException(unchecked)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── IllegalArgumentException
    │   ├── ClassCastException
    │   ├── ArithmeticException
    │   └── ...(绝大多数业务异常都继承这个)
    └── 其他 checked Exception(必须声明或捕获)
        ├── IOException
        │   ├── FileNotFoundException
        │   └── SocketException
        ├── SQLException
        ├── ClassNotFoundException
        └── ...(所有需要显式处理的异常)

三、实际开发中最常见的对比场景

场景属于哪一类你应该怎么处理?(真实项目建议)
除以 0RuntimeException通常不捕获,让它抛出去,由全局异常处理器统一处理
访问 null 对象RuntimeException基本不捕获,修复代码逻辑(防御式编程 + Optional)
读取配置文件不存在FileNotFoundException(checked)必须处理:要么 throws,要么 try-catch 给用户友好提示
数据库连接超时 / SQL 语法错误SQLException(checked)必须处理:回滚事务、记录日志、重试或降级
JVM 堆内存不足OutOfMemoryError无法捕获,基本只能重启或扩容
递归太深导致栈溢出StackOverflowError无法合理捕获,说明代码有问题(尾递归优化、改迭代)
JSON 解析失败(业务输入错误)JsonProcessingException(通常 unchecked)建议捕获,返回友好提示给前端
调用方传了非法参数IllegalArgumentException建议抛出,让调用方修正

四、实际项目中“处理”策略对比(2025–2026 主流做法)

  1. Error 处理原则(几乎不处理)
  • 不要在业务代码里捕获 Error
  • 不要catch (Throwable t) 然后把 Error 当 Exception 处理(非常危险)
  • 正确的做法:让它自然抛出 → 由容器(如 Spring Boot)或监控系统(Sentry、SkyWalking)捕获 → 告警 + 重启 / 扩容
  1. Exception 处理原则(分层处理)
  • Controller 层:统一用 @ExceptionHandlerGlobalExceptionHandler 转换为标准响应
  • Service 层:抛出业务异常(自定义 BizException 继承 RuntimeException)
  • Dao/Repository 层:checked 异常(如 SQLException)通常包装成 unchecked 再抛
  • 工具类/底层:尽量抛出具体异常而不是泛泛的 Exception
// 推荐的业务异常定义方式(2025+ 常见写法)
public class BizException extends RuntimeException {
    private final String code;

    public BizException(String code, String message) {
        super(message);
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

// 使用示例
if (user == null) {
    throw new BizException("USER_404", "用户不存在");
}

五、经典面试追问 & 标准回答

Q1:能不能 catch Error?

能,但不应该。catch Error 通常掩盖了更严重的问题,正确的做法是让它暴露出来。

Q2:为什么 RuntimeException 不需要声明 throws?

因为它是 unchecked exception,设计初衷就是“程序员的编程错误”,应该在开发阶段修复,而不是强迫调用方处理。

Q3:自定义异常应该继承 Exception 还是 RuntimeException?

  • 必须强制调用方处理的 → 继承 Exception(checked)
  • 属于编程错误 / 可选处理的 → 继承 RuntimeException(unchecked) ← 99% 业务异常都用这个

Q4:OutOfMemoryError 能 catch 住然后释放内存吗?

极少数情况下可以(比如 catch OutOfMemoryError 后释放一些大对象),但绝大多数场景下已经晚了,JVM 状态已经非常危险,catch 了也很难恢复。

六、总结一句话口诀(背下来答题快)

Error 是系统病危,基本等死;Exception 是感冒发烧,能治好。

Error → 不捕获,让它死得明白
Exception → 分 checked / unchecked,该捕获捕获,该抛抛

你现在是在准备面试,还是在实际项目中纠结怎么定义异常体系?
可以告诉我具体场景,我可以帮你给出更落地的异常分层方案。

文章已创建 4237

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部