Java 中异常分哪两类,有什么区别?

一则或许对你有用的小广告

欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于 Spring AI + Spring Boot3.x + JDK 21...点击查看;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot3.x + JDK 17...点击查看项目介绍; 演示链接: http://116.62.199.48:7070/;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/

面试考察点

  1. 体系理解:面试官不仅仅是想知道 "受检异常" 和 "非受检异常" 这两个名词,更是想考察你是否理解 Java 异常体系的继承结构ThrowableError/Exception)。

  2. 编译机制:受检异常的 "受检" 是什么意思?这涉及 Java 编译器如何强制处理异常。

  3. 实践能力:实际开发中如何选择异常类型?何时用受检异常、何时用非受检异常?

核心答案

Java 异常分为受检异常和非受检异常两大类。受检异常必须显式处理(try-catchthrows),否则编译不通过;非受检异常可以不处理,由 JVM 在运行时抛出。

对比维度受检异常非受检异常
父类Exception(除 RuntimeExceptionErrorRuntimeException
编译检查✅ 必须处理,否则编译失败❌ 不强制处理
处理方式try-catchthrows可选处理
代表场景IO 错误、数据库异常空指针、数组越界、内存溢出
设计理念可恢复的异常不可恢复或程序 bug

一句话总结:受检异常是 "编译器逼你处理",非受检异常是 "运行时随缘处理"。

深度解析

一、Java 异常体系结构

上图展示了 Java 异常体系的完整继承结构。关键要点:

  • Throwable:所有异常的父类,只有它的子类才能被 throwcatch
  • Error:JVM 级别的严重错误,程序无法恢复(如 OutOfMemoryErrorStackOverflowError
  • Exception:程序可以处理的异常
    • 受检异常IOExceptionSQLException 等,编译器强制处理
    • 非受检异常RuntimeException 及其子类,编译器不强制处理

二、受检异常 vs 非受检异常

import java.io.*;

public class ExceptionDemo {

    // ========== 受检异常:必须处理 ==========
    public void readCheckedFile(String path) {
        // ❌ 编译错误:Unhandled exception type IOException
        // FileReader reader = new FileReader(path);

        // ✅ 方式一:try-catch 捕获
        try {
            FileReader reader = new FileReader(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // ✅ 方式二:throws 声明抛出
    public void readFile(String path) throws IOException {
        FileReader reader = new FileReader(path);
    }

    // ========== 非受检异常:不强制处理 ==========
    public void uncheckedDemo() {
        String str = null;

        // ⚠️ 编译通过,但运行时抛出 NullPointerException
        System.out.println(str.length());

        // 可以选择捕获,也可以不捕获
        try {
            System.out.println(str.length());
        } catch (NullPointerException e) {
            System.out.println("空指针异常");
        }
    }
}

三、常见异常类型

类型异常类触发场景
Error(非受检)OutOfMemoryError内存不足
StackOverflowError栈溢出(无限递归)
NoClassDefFoundError类找不到
RuntimeException(非受检)NullPointerException访问 null 对象
ArrayIndexOutOfBoundsException数组越界
ClassCastException类型转换失败
ArithmeticException算术异常(如除零)
IllegalArgumentException非法参数
受检异常IOExceptionIO 操作失败
SQLException数据库操作失败
ClassNotFoundException类加载失败
InterruptedException线程中断

四、如何选择异常类型?

上图展示了选择异常类型的决策流程。关键原则:

  • 受检异常:调用者可以合理恢复的情况(如文件不存在、网络超时)
  • 非受检异常:程序 bug 或调用者无法处理的情况(如空指针、数组越界)
  • Error:JVM 级别的严重问题,程序不应尝试捕获

五、最佳实践

// ✅ 好的做法:自定义业务异常(继承 RuntimeException)
public class BusinessException extends RuntimeException {
    private String errorCode;

    public BusinessException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
}

// ✅ 好的做法:受检异常转非受检异常(避免污染方法签名)
public void processFile(String path) {
    try {
        readFile(path);
    } catch (IOException e) {
        // 将受检异常包装为非受检异常
        throw new RuntimeException("文件处理失败", e);
    }
}

// ❌ 不好的做法:捕获异常后不处理(吞掉异常)
try {
    doSomething();
} catch (Exception e) {
    // 什么都不做,异常被吞掉了!
}

// ✅ 好的做法:至少记录日志
try {
    doSomething();
} catch (Exception e) {
    log.error("操作失败", e);
    throw new BusinessException("OPERATION_FAILED", "操作失败");
}

面试高频追问

  1. throwthrows 有什么区别?

    关键字位置作用
    throw方法体内抛出一个异常对象
    throws方法签名上声明方法可能抛出的异常类型
  2. finalfinallyfinalize 有什么区别?

    • final:修饰符,类不能继承、方法不能重写、变量不能修改
    • finallytry-catch 的最后一块,无论是否异常都会执行
    • finalizeObject 的方法,GC 回收对象前调用(已废弃)
  3. 为什么要有受检异常?有人说它是 Java 的设计失误,你怎么看?

    争议点:

    • 支持者:强制处理异常,提高代码健壮性
    • 反对者:导致代码冗余,异常传播污染方法签名,实际中常被滥用
    • 趋势:现代框架(Spring)倾向于使用非受检异常

常见面试变体

  • 变体一:"ErrorException 有什么区别?"
  • 变体二:"RuntimeException 和普通 Exception 有什么区别?"
  • 变体三:"如何设计一个自定义异常类?"

记忆口诀

受检异常编译查,必须 try-catch 或 throws; 非受检异常运行时,不强制处理随缘抓; Error 严重难恢复,Exception 可处理。

总结

Java 异常分为受检异常(必须显式处理)和非受检异常(不强制处理)。受检异常适合可恢复的场景,非受检异常适合程序 bug 或无法恢复的场景。实际开发中,推荐使用非受检异常(继承 RuntimeException)设计业务异常,避免受检异常污染方法签名。