臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速7.Google 搜索收录排查:为什么关键词搜得到别人,却搜不到我的网站?Obsidian、双向链接与知识图谱
架构设计
分布式分布式事务RPC原理
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
数据库
mysql01.MySQL基础语法与约束02.MySQL数据库类型03.MySQL多表查询04.MySQL 索引05.MySQL 事务06.MySQL 隔离级别07.MySQL MVCC08.MySQL 锁09.EXPLAIN10.慢查询与 SQL 优化
mysql面试题01.索引问题02.事务与隔离级别03.锁机制04.存储引擎与性能优化05.用户权限、字符集与备份恢复
mysql实战01.EXPLAIN实战02.测试环境菜单误删排查与恢复03.MySQL IN 详解
网络
网络安全1.无线监听网卡苹果M系列虚拟机安装Kali Linux教程3.kali怎么使用RTL8812AU4.kali基础命令
网络基础进程之间怎样通过网络传递数据
网址常用网址
中间键
esElasticsearch 入门
mq
RabbitMQRabbitMQ高级RabbitMQ基础
1. MQ 基础2. MQ 核心角色与代码对应3. MQ Consumer Group 与消费模式4. SOFAMQ 账户变更代码实践5. MQ 可靠性问题6. 消息幂等
redis缓存击穿、穿透与雪崩Redis 分布式锁Redis 缓存问题Redis 数据结构
AI
使用说明
第三方插件&技能简介Archify使用与安装指南geekgeekRun使用OfferFlow使用指南Ponytail使用指南Skills Manager使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发0-学习路线1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.*完成可运行聊天接口11.Function Calling、Tool Calling 原理12.用 Java 方法定义 Tool13.工具参数设计和校验14.实现订单查询工具15.实现物流查询工具16.实现库存查询工具17.多个工具自动选择18.工具异常、超时、重试
diy主机
主机零件学习1.CPU2.主板3.内存4.显卡5.硬盘6.散热7.风扇8.机箱热门型号价格表
捡垃圾diy-13600kf+5060diy-14600kf+5070
English高频词汇英文基础语法简单句子音标
GitGit常用命令Sublime Merge 使用指南
Java
工具库Caffeine本地缓存Reactor教程
工作实践如何使用@Async线程池使用与排查线上服务 CPU 使用率过高排查压测下RPC接口大量失败排查记录
基础1.HashMap2.ConcurrentHashMap3.HashSet4.final5.static6.volatile7.AtomicInteger8.String动态代理反射锁与线程同步线程注解
面试题1.HashMap 存储在方法内是线程安全的吗2.ArrayList参数传递与引用重新赋值3.== 和 equals 的区别、hashcode4.重载和重写的区别5.String、StringBuffer、StringBuilder区别及使用场景6.接口和抽象类7.ArrayList 与 LinkedList 的区别和扩容机制8.HashMap 和 Hashtable 的区别及底层实现9.Object 和 Objects 的区别10.ThreadLocal的原理和使用场景11.字节码是什么及作用12.Java类加载器有哪些13.双亲委派模型14.Java异常体系15.GC是什么16.GC如何判断对象可以被回收
面试总结0.HR面试模板1.面试一2.面试二3.面试三笔试
网络与 IO(黑马程序员-纯理论)01-网络编程基础02-IO模型与零拷贝03-BIO04-NIO基础与缓冲区05-NIO通道与选择器06-AIO
juc00-目录01-进程与线程02-线程同步与通信03-并发设计模式04-Java内存模型05-CAS与原子类06-ThreadLocal07-阻塞队列08-线程池使用与原理09-任务调度10-ForkJoin与享元模式11-AQS12-ReentrantLock13-读写锁14-并发协作工具15-ConcurrentHashMap16-CopyOnWrite与同步容器17-SkipListMap18-非阻塞队列
juc(工作常见)Java并发编程三个重点拆解
jvm1. JVM运行时数据区
Spring1.Spring IoC、DI与构造方法注入CGLIB、Byte Buddy 与 Spring AOPSpring 常用注解详解Spring Bean 生命周期
Spring MVC异常处理机制
linux常用命令
mac快捷键终端常用操作
python
基础01.Python是什么,为什么要安装Python02.Python项目中.venv是什么03.Python基础语法
基础实战sql替换
HTTPXHRMS员工详情接口调用(Python HTTPX)
windowswindows激活

一、💡 一句话理解

核心结论

Java 中所有异常和错误都继承 Throwable。Error 通常表示程序难以恢复的严重问题;Exception 表示程序可能处理的问题,其中又分为受检异常和非受检异常。

二、🧭 理论:异常体系

2.1 继承结构

Throwable
├── Error                  严重问题,程序通常不处理
└── Exception              程序可能处理的问题
    ├── RuntimeException   非受检异常
    └── 其他 Exception     受检异常
类型常见例子编译器是否强制处理一般怎么做
ErrorOutOfMemoryError否排查运行环境或系统问题
受检异常IOException是使用 catch 处理,或用 throws 声明
非受检异常NullPointerException否优先修复代码逻辑

判断规则

RuntimeException 及其子类、Error 及其子类属于非受检异常;其余异常类属于受检异常。

2.2 三类问题一般怎么发生

2.2.1 Error:运行环境撑不住了

例如程序不断创建对象并保留引用,导致 JVM 堆内存耗尽:

List<byte[]> data = new ArrayList<>();
 
while (true) {
    // 对象一直被 data 引用,无法被垃圾回收。
    data.add(new byte[1024 * 1024]);
}

最终可能抛出 OutOfMemoryError。这类问题通常不靠 catch 恢复,而要检查内存泄漏、数据量以及 JVM 内存配置。

2.2.2 受检异常:外部条件不可靠

// 文件可能不存在、没有权限,或者读取过程中发生故障。
String content = Files.readString(Path.of("demo.txt"));

文件状态不完全由程序控制,因此 Files.readString() 可能抛出 IOException。调用方必须使用 catch 处理,或者使用 throws 继续向上声明,否则无法通过编译。

2.2.3 非受检异常:代码逻辑有问题

String name = null;
 
// null 没有指向真实对象,调用方法时会抛出 NullPointerException。
System.out.println(name.length());

编译器不会强制处理 NullPointerException。这类异常通常意味着参数校验或代码逻辑存在问题,应优先修复原因,而不是简单地用 catch 忽略。

快速记忆

Error:环境撑不住;受检异常:外部情况可能失败;非受检异常:代码逻辑需要修复。

三、⚙️ 理论:异常如何传播

3.1 沿调用栈向上寻找处理者

“调用栈向上”是指:从当前出错的方法,回到调用它的方法,再回到更上一层调用者,并不是在源代码中向上寻找。

例如下面的调用关系:

main()
  └── orderService()
        └── queryProduct()
public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            orderService();
        } catch (IllegalArgumentException e) {
            // queryProduct 和 orderService 都没有处理,最终由 main 处理。
            System.out.println("main 处理异常:" + e.getMessage());
        }
    }
 
    static void orderService() {
        // 调用查询商品的方法。
        queryProduct();
    }
 
    static void queryProduct() {
        // 当前方法主动抛出异常,但没有在本方法中捕获。
        throw new IllegalArgumentException("商品编号错误");
    }
}

异常的寻找过程是:

queryProduct 抛出异常
        ↑ 本方法没有 catch
orderService 也没有 catch
        ↑ 继续回到调用者
main 找到匹配的 catch
        ↑
异常被处理

如果 main() 也没有匹配的 catch,异常就没有人处理,当前线程会结束,并打印异常堆栈。

记忆口诀

哪里出错,从哪里开始找;本层不处理,就交给上一层调用者。

3.2 多个 catch 为什么先写子类

“先匹配小异常”说的是同一个 try 后有多个 catch 时,要先写范围小的子类异常,再写范围大的父类异常:

try {
    // 可能发生异常的代码。
} catch (NullPointerException e) {
    // 先处理更具体的子类异常。
} catch (RuntimeException e) {
    // 再处理范围更大的父类异常。
}

如果先写 RuntimeException,它已经可以捕获后面的 NullPointerException,因此更具体的 catch 永远无法执行,编译器会报错。

四、🚀 实践:最小示例

4.1 可以拿来干什么

使用异常可以把正常业务流程和错误处理分开。与文件、网络等外部资源交互时,常见做法是捕获可以恢复的受检异常;参数错误等编程问题通常使用非受检异常表示。

4.2 完整实践:捕获受检异常

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
 
public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            // readString 可能抛出 IOException,因此必须捕获或继续声明抛出。
            String content = Files.readString(Path.of("demo.txt"));
            System.out.println(content);
        } catch (IOException e) {
            // 这里只处理能够预料到的文件读取失败。
            System.out.println("文件读取失败:" + e.getMessage());
        }
    }
}

文件存在时输出文件内容;文件不存在或无法读取时,进入 catch 并输出错误信息。

五、🎤 面试时怎么流利地说

5.1 30 秒标准回答

Java 中所有异常和错误都继承 Throwable,下面主要分为 Error 和 Exception。Error 通常是 JVM 或系统层面的严重问题,程序一般不主动处理。Exception 是程序可能处理的问题,其中 RuntimeException 及其子类属于非受检异常,编译器不强制处理;其他 Exception 通常属于受检异常,必须使用 try-catch 捕获或者通过 throws 声明。

5.2 高频追问

throw 和 throws 有什么区别?

  • throw 用在方法体内,真正抛出一个异常对象。
  • throws 写在方法声明上,说明这个方法可能向外抛出哪些异常。

可以直接捕获 Exception 吗?

可以,但不建议不分场景地捕获。捕获范围过大会掩盖具体错误,也不利于采用有针对性的恢复方式。

六、📌 总结

  • 异常体系的根类是 Throwable。
  • Throwable 的两个主要分支是 Error 和 Exception。
  • 受检异常必须 catch 或 throws。
  • RuntimeException 和 Error 都属于非受检异常。
  • 异常会沿调用栈向上寻找匹配的处理者。

七、📚 官方资料

延伸阅读

Web 项目中的统一异常转换和响应处理,可以继续阅读 异常处理机制。