checked exception编译时强制处理,如ioexception;unchecked exception是runtimeexception子类,运行时抛出,如nullpointerexception;error如outofmemoryerror不应捕获。

Java中Checked Exception和Unchecked Exception的区别
Java异常分层的核心在于编译器是否强制你处理——这直接决定你写不写 try-catch 或声明 throws。
Checked Exception(如 IOException、SQLException)继承自 Exception 但不继承 RuntimeException,编译器会报错:「Unhandled exception type XXX」,必须显式捕获或上抛。
Unchecked Exception(如 NullPointerException、ArrayIndexOutOfBoundsException)是 RuntimeException 及其子类,运行时才暴露,编译器不管,但不代表可以忽略。
- 业务逻辑中主动抛出的异常,建议用自定义
RuntimeException子类(如OrderNotFoundException),避免污染方法签名 -
Error(如OutOfMemoryError)不属于“异常处理”范畴,不应被捕获或重试,JVM层面问题,程序通常无法恢复 - 别把
Exception当万能 catch:捕获顶层Exception容易掩盖真正需要关注的异常类型,也违反里氏替换原则
如何设计合理的自定义异常层级
一个清晰的异常体系不是为了多建几个类,而是让调用方能基于类型做差异化响应——比如重试、降级、告警或用户提示。
立即学习“Java免费学习笔记(深入)”;
推荐按「可恢复性」和「责任归属」分两层:
- 继承
RuntimeException的业务异常(如InsufficientBalanceException),代表输入/状态非法,调用方应修正参数或流程,不重试 - 继承
Exception的系统异常(如PaymentServiceUnavailableException),代表外部依赖临时不可用,调用方可能需重试或走备用通道
所有自定义异常都应提供带 String message 和 Throwable cause 的构造函数,并在必要时携带业务字段(如订单ID、错误码):
public class PaymentFailureException extends Exception {
private final String orderId;
public PaymentFailureException(String orderId, String message, Throwable cause) {
super(message, cause);
this.orderId = orderId;
}
}
catch块顺序与多异常捕获的陷阱
Java按 catch 块从上到下的顺序匹配,父类异常写在前面会导致子类永远捕获不到——编译器会直接报错「exception XXX has already been caught」。
- 必须先 catch 子类,再 catch 父类:例如
catch (FileNotFoundException e)要放在catch (IOException e)前面 - Java 7+ 支持多异常捕获语法:
catch (IOException | SQLException e),但要求这些异常无继承关系,且共用同一处理逻辑 - 不要在
catch里只写e.printStackTrace():日志丢失上下文,无法定位问题;应使用 SLF4J 等框架记录logger.error("Failed to process order {}", orderId, e)
finally和try-with-resources的真实作用边界
finally 不是“一定会执行”的保险柜——JVM 强制退出(System.exit())、线程被中断、或发生 OutOfMemoryError 时,finally 可能跳过。
资源释放优先用 try-with-resources,它基于 AutoCloseable 接口,比手写 finally 更可靠:
- 多个资源用分号隔开,关闭顺序与声明顺序相反(后声明的先关闭)
- 如果
close()抛异常,且try块已有异常,后者会被抑制(suppressed),可通过Throwable.getSuppressed()获取 - 不要在
try-with-resources的资源声明中调用可能抛异常的方法(如new FileInputStream(getFilePath())),否则异常可能掩盖资源初始化失败的真正原因
异常分类处理的关键不在“分得多细”,而在于每种异常类型背后是否对应明确的应对策略。很多人把异常当错误日志用,结果 catch 了一堆却不知道下一步该重试、回滚,还是提示用户换手机号——这才是分层设计真正要解决的问题。








