Java异常体系以Throwable为根,分为Error(如OOM、栈溢出,不可恢复,不建议捕获)和Exception;后者再分受检异常(编译强制处理,如IOException)和非受检异常(即RuntimeException,如空指针,应预防而非捕获)。

Java异常体系是面试高频考点,也是日常开发中容易出错的关键环节。理解清楚异常分类、处理机制和最佳实践,不仅能应对面试提问,更能写出健壮、可维护的代码。
一、异常的分类:Throwable是根,两大分支要分清
Java所有异常都继承自Throwable类,它有两个直接子类:Error 和 Exception。
-
Error:表示JVM无法恢复的严重问题,如
OutOfMemoryError、StackOverflowError。程序一般不捕获也不处理,应通过调优或重构避免。 -
Exception:分为受检异常(Checked Exception)和非受检异常(Unchecked Exception):
- 受检异常:编译器强制要求处理(try-catch 或 throws),如
IOException、SQLException。代表外部可预期但不可控的问题。 - 非受检异常:即
RuntimeException及其子类,如NullPointerException、ArrayIndexOutOfBoundsException。编译器不强制处理,通常反映程序逻辑缺陷,应通过预防而非捕获来解决。
- 受检异常:编译器强制要求处理(try-catch 或 throws),如
二、异常处理的核心原则:捕获要精准,抛出要有据
不是所有异常都适合try-catch,也不是所有方法都要throws。关键看责任归属与恢复能力:
OpenJDK 24 Windows x64 官方参考实现(24+36),适合 Windows 环境进行 Java 开发、编译、运行和版本兼容性验证。
- 只捕获能真正处理的异常;避免空catch或仅打印堆栈后继续执行。
- 不要用异常控制业务流程(比如用
NumberFormatException做字符串是否为数字的判断)。 - 自定义异常时,优先继承
RuntimeException(运行时异常),除非该异常确实属于调用方必须显式应对的外部契约(如支付接口超时)。 - 在方法签名中声明
throws时,应只抛出上层真正需要关心的异常类型,避免暴露底层实现细节(如把SQLException直接抛给Service层)。
三、常见误区与改进写法
这些写法在面试和代码审查中常被指出:
立即学习“Java免费学习笔记(深入)”;
-
“吞掉”异常:
catch (Exception e) { }—— 日志都没留,问题无法定位。✅ 改为记录日志并考虑是否重抛或转为业务异常。 -
异常信息丢失:
throw new BusinessException("操作失败")—— 丢掉了原始堆栈。✅ 使用构造函数链式传递:new BusinessException("操作失败", e)。 - finally里盲目return:导致try/catch中的return被覆盖,异常信息丢失。✅ finally中只做资源清理,避免return或throw。
-
多异常重复捕获:多个catch块内容雷同。✅ Java 7+支持多异常捕获:
catch (IOException | SQLException e)。
四、现代实践建议:配合Lombok、Optional与统一异常处理
提升异常处理的简洁性与一致性:
- 用
@SneakyThrows(Lombok)简化受检异常抛出,适用于明确无需上层感知的场景(如工具类中读取本地配置文件)。 - 对可能为空的对象,优先用
Optional替代null检查,从源头减少NullPointerException。 - 在Web项目中,使用
@ControllerAdvice + @ExceptionHandler统一处理全局异常,分离业务逻辑与错误响应构建。 - 日志框架中启用
%ex或log.error("msg", e)完整输出堆栈,便于排查。

















