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

Error 和 Exception 的核心区别(Java 实战中最常被问到、最容易混淆的概念之一)

一句话总结(面试/写代码时最简洁的回答):

  • Error:严重系统错误,通常意味着 JVM 层面出了大问题,程序基本无药可救,不建议捕获。
  • Exception:程序可以预期、可以处理的异常情况,业务逻辑或输入问题,应该捕获并处理。

1. 继承结构对比(最直观的图)

Throwable(所有可抛出的根)
├── Error
│   ├── VirtualMachineError
│   │   ├── OutOfMemoryError
│   │   ├── StackOverflowError
│   │   └── InternalError
│   ├── LinkageError
│   │   ├── NoClassDefFoundError
│   │   ├── ClassNotFoundError(极少见)
│   │   └── ...
│   └── AWTError / ThreadDeath / ...
└── Exception
    ├── RuntimeException(非检查异常)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   ├── IllegalArgumentException
    │   ├── ClassCastException
    │   ├── ArithmeticException
    │   ├── ConcurrentModificationException
    │   └── ...
    └── 其他 checked Exception(检查异常)
        ├── IOException
        │   ├── FileNotFoundException
        │   ├── SocketException
        │   └── ...
        ├── SQLException
        ├── ParseException
        ├── InterruptedException
        └── ...

2. 核心区别对比表(背下来就能应对 90% 的相关问题)

维度ErrorExceptionRuntimeException(Exception 的子类)
严重程度极其严重,通常不可恢复可预期、可恢复可预期,但属于编程错误
是否必须处理不建议捕获checked → 必须声明/捕获
unchecked → 可选
不强制处理(unchecked)
典型场景JVM 内存耗尽、栈溢出、类加载失败文件不存在、网络断开、数据库连接失败空指针、下标越界、类型转换错误
出现时机系统/虚拟机级别应用程序/业务逻辑级别程序员编码 Bug 级别
是否应该修复代码基本不需要改代码,调 JVM 参数或硬件通常要改代码或加防护必须改代码(Bug)
常见处理方式记录日志 → 报警 → 重启节点try-catch / throws 处理或转换业务异常基本不 catch,放着让上层或全局异常处理器处理
是否继承自 Throwable
是否继承自 Exception

3. 实战中最常见的错误用法(你很可能正在犯)

// 错误写法1:捕获 Error(几乎永远不要这么写)
try {
    // 一些代码
} catch (Error e) {           // ← 危险!
    // 你以为你能处理 OutOfMemoryError?
    // 大部分情况下捕获了也无能为力,还可能掩盖真正的问题
}

// 错误写法2:把所有异常都当成 RuntimeException 处理
try {
    // ...
} catch (RuntimeException e) {
    // 你把 IOException、SQLException 也漏掉了!
}

// 更危险写法(很多老项目还能看到)
try {
    // ...
} catch (Exception e) {       // 捕获了所有 Exception,包括 checked
    // 直接吞掉或只打日志,什么都不做
    log.error("出错了", e);
}

4. 推荐的实战处理策略(2025-2026 年主流写法)

// 1. 正常业务异常(checked) → 显式处理或转换
public User getUser(Long id) throws UserNotFoundException {
    User user = userRepo.findById(id);
    if (user == null) {
        throw new UserNotFoundException(id);   // 自定义业务异常
    }
    return user;
}

// 2. 编程错误(NPE、越界等) → 基本不 catch,放出去
public void process(int index) {
    list.get(index);  // 如果 index 非法 → 抛出 IndexOutOfBoundsException
                      // 上层统一处理或让程序 crash(开发/测试阶段)
}

// 3. 全局统一异常处理(Spring Boot 推荐)
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NullPointerException.class)
    public ResponseEntity<?> handleNPE(NullPointerException ex) {
        // 返回 500 + 内部错误提示
        return ResponseEntity
            .internalServerError()
            .body("系统繁忙,请稍后重试");
    }

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<?> handleBusiness(BusinessException ex) {
        // 返回 400 + 业务提示
        return ResponseEntity.badRequest().body(ex.getMessage());
    }

    // 兜底
    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleAll(Exception ex) {
        log.error("未预期异常", ex);
        return ResponseEntity.internalServerError().body("服务器异常");
    }
}

5. 快速记忆口诀

  • Error → “JVM 炸了,程序员无能为力”
  • checked Exception → “你知道会发生,必须提前说(throws)或处理(try)”
  • RuntimeException → “程序员的锅,应该提前防(校验参数、判空等)”

一句话总结面试最稳的回答:

Error 表示严重的系统错误,程序员通常无法恢复,不建议捕获;
Exception 表示程序可以处理的异常情况,分为 checked(必须处理)和 unchecked(RuntimeException,通常是编程错误)。

你项目里有没有自定义过业务异常体系?
是全部继承 RuntimeException,还是有一部分 checked Exception?
或者你们是怎么处理全局异常的?可以聊聊你们的实际做法~

文章已创建 4206

发表回复

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

相关文章

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

返回顶部