A异常会被完全丢弃,因为Java规范规定finally块中抛出的异常会覆盖try或catch中已抛出的异常;JVM直接丢弃A,仅传播B,且不将其设为cause或suppressed。

Java中,如果try块抛出A异常,而finally块又抛出B异常,那么A异常会被**完全丢弃**,调用栈中最终只看到B异常——A异常对象虽然还存在,但不再可访问,也不会出现在任何异常链中。
为什么A异常会消失?
这是Java语言规范明确规定的:当finally块以throw语句(或return、break等提前退出机制)结束时,它会“覆盖”try或catch中已抛出的异常。JVM在执行到finally的throw时,会直接把当前正在传播的A异常丢弃,转而将B异常作为唯一异常向上抛出。
这和“异常被吞掉”不同——A异常不是被静默捕获,而是被主动替换。JVM不会尝试把A设为B的cause,也不会记录在suppressed exceptions里(那是try-with-resources中自动添加抑制异常的机制,不适用于普通finally)。
代码示例说明
看这段典型代码:
立即学习“Java免费学习笔记(深入)”;
public static void example() {
try {
throw new RuntimeException("A");
} finally {
throw new IllegalArgumentException("B");
}
}
调用example()后,堆栈跟踪只显示IllegalArgumentException: B,完全看不到"RuntimeException: A"的痕迹。
如何保留A异常?
如果业务上需要同时暴露两个异常(比如资源清理失败不能掩盖主逻辑错误),必须手动处理:
- 在finally中捕获并记录A异常(例如打日志),再抛B;
- 或在try/catch结构外保存A,然后在finally中将其设为B的suppressed exception(需手动调用
b.addSuppressed(a)); - 更推荐的方式是避免在finally中抛异常——清理逻辑应尽量设计为不抛出检查异常,非检查异常(如NPE)应视为bug修复,而非正常流程。
对比:try-with-resources的行为
注意,这和try-with-resources中“主异常 + 抑制异常”的行为不同。后者是专门设计的增强机制:资源关闭抛出的异常会自动调用addSuppressed()附加到主异常上。而普通finally没有这种自动支持,一切由开发者控制。


















