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

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

这是 Java 面试和实际开发中最常被问到的问题之一,但很多人在实际 coding 时还是会混淆。下面从定义 → 分类 → 是否可恢复 → 处理策略 → 真实场景完整对比,并给出最实用的判断标准。

核心一句话总结(面试/工作最常用)

  • Exception:程序自己或外部环境造成的可预料、可恢复的异常情况,应该被捕获和处理。
  • ErrorJVM/操作系统/硬件层面的严重错误正常应用不应该尝试捕获,通常也无法恢复,程序基本要挂。

详细对比表(最清晰版)

对比维度ExceptionError
父类java.lang.Exceptionjava.lang.Error
共同父类ThrowableThrowable
是否 checked分 checked 和 unchecked(RuntimeException 子类)全是 unchecked
发生时机主要运行时(少量编译时,如 checked)运行时(极少编译时)
是否可恢复大多数可以恢复(合理处理后继续运行)几乎不可恢复(恢复基本无意义)
合理做法应该捕获、处理或抛出给上层不应该捕获(除非极特殊场景,如监控)
典型代表NullPointerException, IOException, SQLException, IllegalArgumentExceptionOutOfMemoryError, StackOverflowError, VirtualMachineError
谁的责任程序员 / 业务逻辑 / 输入参数 / 外部资源JVM / 操作系统 / 硬件 / 环境
出现频率(生产环境)非常高(每天都可能)较低,但一旦出现基本是致命的

真实项目中最常见的 Error 子类(见过就认得出)

java.lang.OutOfMemoryError
  ├─ Java heap space           最常见:堆内存不足
  ├─ GC overhead limit exceeded   GC 太频繁,回收效果差
  ├─ PermGen space / Metaspace(Java8+)  元空间/永久代溢出
  └─ Native memory                 本地内存耗尽

java.lang.StackOverflowError       递归太深或线程栈太小

java.lang.VirtualMachineError      虚拟机内部错误(极少见)

真实项目中最常见的 Exception 子类分类

Checked Exception(必须处理,否则编译不通过)

  • IOException / FileNotFoundException
  • SQLException
  • ClassNotFoundException

Unchecked Exception(RuntimeException 及其子类,运行时才暴露)

  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • IllegalArgumentException
  • ClassCastException
  • NumberFormatException
  • ArithmeticException(/0)
  • ConcurrentModificationException

实战代码对比(一看就懂)

// 应该捕获的 Exception(正常业务场景)
public String readConfig(String path) {
    try {
        return Files.readString(Paths.get(path));
    } catch (IOException e) {           // ← 合理捕获
        log.error("读取配置文件失败,使用默认配置", e);
        return DEFAULT_CONFIG;
    }
}

// 不应该捕获的 Error(几乎没人这么写)
public void processBigData() {
    try {
        // 假设这里分配超大内存
        byte[] huge = new byte[Integer.MAX_VALUE];
    } catch (OutOfMemoryError e) {      // ← 极不推荐
        // 这里基本救不回来,还可能让问题更复杂
        log.fatal("内存耗尽", e);
        System.exit(1);                 // 直接退出更合理
    }
}

真实项目推荐做法

// 推荐:只捕获 Exception,不捕获 Error
public void someServiceMethod() {
    try {
        // 业务代码
    } catch (Exception e) {             // 通常捕获 Exception
        if (e instanceof RuntimeException) {
            // 可能是 bug,记录详细日志
            log.error("业务运行时异常", e);
            throw e;                    // 或包装后抛出
        } else {
            // checked exception,尝试恢复或友好提示
            log.warn("可预期的异常", e);
            // 返回降级结果
        }
    }
    // 注意:这里不会写 catch(Error e) 除非你真的在做 JVM 监控工具
}

面试/工作中最常被问的几个追问

  1. 能不能 catch Error?
    → 语法上可以,但强烈不推荐。捕获后也基本救不回来,还可能掩盖真正问题。
  2. OutOfMemoryError 能不能恢复?
    → 极个别场景可以(比如调大堆、降级功能),但绝大多数情况只能重启或缩容。
  3. 为什么 RuntimeException 也叫 unchecked exception?
    → 因为它和 Error 一样,不需要在方法上声明 throws,属于“程序员的 bug”居多。
  4. 生产环境遇到 OutOfMemoryError 怎么办?
    → 先看是哪种 OOM(heap / metaspace / native)
    → 收集 dump(jmap / jcmd / arthas)
    → 分析原因(内存泄漏?大对象?GC 策略?)
    → 临时加大堆 / 临时降级 / 重启
    → 长期修复代码或架构

一句话记住:

“Exception 是我的锅,我来修;Error 是 JVM/机器的锅,我尽力跑路”

有哪种具体场景(比如 OOM 排查、自定义异常、Spring 中的异常处理)还想再深入聊聊?

文章已创建 4298

发表回复

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

相关文章

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

返回顶部