exceptionally 是 CompletableFuture 中捕获运行时异常并提供兜底值的回调方法,不中断链式调用;仅对 RuntimeException 有效,需手动包装受检异常为 CompletionException;异常必须发生在异步阶段内部,返回值类型须严格匹配前序结果类型。

exceptionally 是 CompletableFuture 异步链中专门用来捕获未检查异常(RuntimeException 及其子类)的回调方法。它不中断链式调用,而是提供一个兜底值,让后续操作(如 thenApply、thenAccept)仍能继续执行。
只响应运行时异常,对受检异常无效
CompletableFuture 的计算函数(如 Supplier、Function)在签名上不声明抛出受检异常,所以编译器不允许直接 throw Exception。如果业务逻辑里有 IO 或 SQL 等可能抛受检异常的操作,必须手动包装成运行时异常:
- 用
new RuntimeException(e)包装 - 或更规范地用
new CompletionException(e)(推荐,保留原始异常栈) - 不能依赖 exceptionally 捕获
IOException、SQLException这类原生受检异常
异常必须发生在异步阶段内部
exceptionally 只能捕获在 supplyAsync、thenApply、thenCompose 等异步阶段 lambda 中抛出的异常。如果流操作写在主线程里,比如先 list.stream().map(...).collect() 再传给 completedFuture,那异常根本不会进入 CompletableFuture 的执行流程,exceptionally 就完全不起作用。
- ✅ 正确:整个流处理包进
supplyAsync(() -> { ... stream.collect() ... }) - ❌ 错误:在外部执行流操作,再把结果塞进 future
- 常见陷阱:
map(i -> i.toString())中i为 null 导致NullPointerException,这类运行时异常可被 exceptionally 捕获
返回值类型必须严格匹配前序结果类型
exceptionally 的泛型参数 Function<Throwable, T> 中的 T 必须和前序阶段完成的值类型一致。比如前一步是 CompletableFuture<String>,那 exceptionally 就必须返回 String,不能返回 Integer 或 null(除非目标类型本身允许 null)。
立即学习“Java免费学习笔记(深入)”;
- 若前序返回
List<User>,exceptionally 应返回Collections.emptyList()或其他合法List<User> - 返回类型不匹配会导致编译错误,不是运行时问题
- 它返回的是一个新的
CompletableFuture<T>,原链不会中断
适合做统一兜底,不适合复杂分支处理
exceptionally 的定位是“失败时给个默认值”,比如远程调用超时返回缓存、列表处理出错返回空集合、字符串转换失败返回占位符。它不区分异常类型,也不感知是哪个阶段出的问题。
- 需要按异常类型分别处理(如
TimeoutException重试,NullPointerException返回默认值),该用handle - 需要记录日志 + 清理资源 + 不改变结果,该用
whenComplete - exceptionally 被触发后,后续的
thenApply会收到它返回的兜底值,而不是异常


















