Java异常体系以Throwable为根,但业务开发应聚焦Exception(含受检与非受检),避免捕获Error或直接使用Throwable;自定义异常优先继承RuntimeException,明确区分错误类型与语义。

Java异常体系的核心是Throwable,它是所有异常和错误的根类,但本身不用于直接捕获或抛出。真正需要关注的是它的两个直接子类:Error 和 Exception——它们代表两类性质完全不同的问题:一类是程序无法也不应干预的系统级崩溃,另一类是开发者应当识别、处理甚至主动抛出的业务或逻辑异常。
Throwable不是用来catch的“兜底类”
虽然语法上允许写 catch (Throwable t),但这是一种危险习惯。Throwable混杂了两类信号:一类是JVM已濒临崩溃(如OutOfMemoryError),另一类是可恢复的业务异常(如IOException)。用同一个catch块吞下全部,等于把火灾警报和门铃响声当成一回事。
- 编译器允许 throw new Throwable(),但无实际意义;它没有提供额外语义,反而模糊问题本质
- 监控工具、AOP切面等底层设施可能需要统一捕获Throwable,但业务代码中应避免
- 日志框架若默认记录Throwable,需确保后续能区分Error与Exception,否则告警会失真
Error代表JVM级严重故障,不应捕获
Error是JVM自身出现问题的信号,比如StackOverflowError(栈溢出)、NoClassDefFoundError(类加载失败)、OutOfMemoryError(堆内存耗尽)。这些不是程序逻辑缺陷,而是运行环境已不可靠。
- 捕获OutOfMemoryError后试图打日志或释放资源,大概率失败——连创建日志对象的内存都没有
- NoClassDefFoundError常被误认为ClassNotFoundException;前者是类初始化阶段失败(如static块抛异常),属于Error,不可恢复;后者是类加载时未找到字节码,属于Exception,可重试或提示配置问题
- 生产环境频繁出现Error被捕获,往往说明系统设计存在隐患,比如内存泄漏未暴露、类加载器隔离不当
Exception才是业务开发的核心战场
Exception分为两类:受检异常(Checked Exception)和非受检异常(Unchecked Exception,即RuntimeException及其子类)。划分依据不是“是否在运行时发生”,而是调用方是否有能力且应该介入处理。
立即学习“Java免费学习笔记(深入)”;
- 受检异常(如IOException、SQLException):表示外部依赖可能失败,且失败后无法自动恢复。调用方必须决定是重试、降级、提示用户,还是向上透传。编译器强制处理,是API契约的一部分
- 运行时异常(如NullPointerException、IllegalArgumentException、IndexOutOfBoundsException):暴露的是代码缺陷或非法输入,属于bug范畴。不强制try-catch,但应通过校验、断言、早抛早知等方式主动暴露,而不是放任静默失败
- 自定义异常建议继承RuntimeException:比如InsufficientBalanceException、InvalidOrderStatusException。这样既保持语义清晰,又不污染方法签名;若确实需要调用方强制处理(如支付失败必须触发退款流程),才继承Exception
别混淆Optional和RuntimeException的职责
Optional解决的是“值可能为空”的建模问题,用于替代null返回值,提升API可读性;而RuntimeException解决的是“参数非法”“状态违例”等逻辑错误。用Optional包裹一个本该抛IllegalArgumentException的地方,等于把错误藏进容器里,掩盖了问题根源。
- 传入null且业务逻辑明确不允许?直接抛NullPointerException或IllegalArgumentException
- 集合get(index)越界?抛IndexOutOfBoundsException,而不是返回Optional.empty()
- 参数格式校验失败?用IllegalArgumentException带明确定义的消息,比if-else后return null更利于调试和监控


















