Java异常沿调用栈自动向上抛出,未捕获则终止程序;受检异常必须声明或处理,运行时异常需主动预防而非忽略;跨层传递应保留原始堆栈,拦截决策取决于业务语义。
Java多层调用中抛异常,本质是异常沿调用栈向上传播的过程。只要某一层没捕获,它就会自动“冒泡”到上一层方法,直到被处理或到达main线程由JVM终止程序。关键不
是“怎么拦住”,而是“在哪拦、怎么拦、拦完做什么”。
当方法A调用B,B调用C,C中抛出NullPointerException:
throws且没catch → 异常继续上抛至A这个过程不依赖return或显式传递,是JVM内置机制,靠字节码中的异常表(Exception Table)驱动。
比如FileReader构造可能抛FileNotFoundException(受检异常):
new FileReader("x.txt"),但没try-catch,编译直接报错throws FileNotFoundException
throws,那B调用C时,也得处理——要么自己catch,要么继续throws向上推这种强制链路设计,是为了提醒开发者:外部依赖(IO/DB/网络)出问题很常见,不能假装看不见。
NullPointerException、ArrayIndexOutOfBoundsException这类非受检异常,编译器不管,但它们暴露的是代码缺陷:
catch (RuntimeException e) { }空吞异常——等于掩盖bug避免用throw new RuntimeException("出错了")掩盖根源。推荐方式:
throw e;(不丢失堆栈)throw new ServiceException("订单创建失败", e);,构造时传入causee.getCause()和e.printStackTrace()可逐层追溯原始错误点日志记录时务必打全堆栈,否则排查多层调用问题会卡在“不知道谁先错的”。
基本上就这些。传播本身很简单,难的是判断哪一层该拦截、拦截后是恢复、降级还是告警,这取决于异常类型和业务语义。