Java中异常传播是JVM沿调用栈逐层上抛至首个匹配catch的过程;未捕获则线程终止。RuntimeException无需声明但照常传播,IOException必须声明或处理。finally抛异常会覆盖原异常,导致信息丢失。

递归函数里的异常不是“卡在某一层”,而是会沿着调用栈逐层向上传播,直到被某层的 try-catch 捕获,或最终到达主线程导致程序终止。关键在于理解传播路径和控制时机。
异常从哪一层开始传播?
只要某次递归调用中抛出了未捕获的异常(比如除零、空指针、数组越界),它就不会继续向下递归,而是立刻中断当前层级,并把异常对象“扔给”它的直接调用者——也就是上一层递归函数。这个过程持续向上,像水往高处流一样逆着调用链回溯。
- 如果第5层递归中发生
NullPointerException,它先传给第4层 - 第4层没写
catch?继续传给第3层 - 直到某层有匹配的
catch,或者传到最外层入口方法(如main)仍未被捕获,JVM 就打印堆栈并终止
在哪一层捕获更合理?
不是越早捕获越好,得看业务意图:
- 想局部恢复:比如读文件失败时,只跳过当前子任务,继续处理其他分支——就在该递归层级加
try-catch - 想统一兜底:比如整个树遍历中途出错,需要记录错误并返回默认结果——在最外层入口处捕获
- 不想吞掉问题:捕获后不做实质处理,只是加日志再
throw或throw new XXXException(...)包装重抛,让上层决定
避免栈溢出干扰异常判断
StackOverflowError 是 Error,不是 Exception,它无法被常规 try-catch(Exception e) 捕获(除非显式写 catch(Error e),但不推荐)。这意味着:
- 递归深度失控时,异常传播机制本身已失效
- 真正要防的是逻辑缺陷:漏写 base case、终止条件写错、参数未递减等
- 可加深度计数器或使用迭代替代深层递归,比依赖异常捕获更可靠
资源清理别依赖 finally?
递归中每个层级都可能执行 finally,但要注意:如果异常发生在第10层,前9层的 finally 仍会按栈逆序依次执行(LIFO)。不过,若某层 finally 也抛异常,它会覆盖原异常(除非用 addSuppressed)。
- 适合做轻量清理:关流、释放锁、打日志
- 避免在
finally里再调递归或做复杂 IO,否则可能引发二次异常 - 对必须释放的资源(如文件句柄),优先用 try-with-resources,比手动
finally更安全

















