exceptionally用于异常时返回兜底值,不改变原CompletableFuture状态;仅响应未捕获异常,返回新完成的CompletableFuture,参数函数需返回同类型值,内部抛异常则新future以该异常完成。

CompletableFuture 的 exceptionally 方法用于在异步任务抛出异常时提供兜底处理逻辑,它不会影响原始 CompletableFuture 的完成状态,而是返回一个新的、已完成的 CompletableFuture(包含异常处理后的结果)。
exceptionally 的作用和特点
它只在前序阶段发生 未捕获的异常 时触发,类似 try-catch 中的 catch 块。注意:它不处理 cancel 或 timeout 场景,也不改变原 CompletableFuture;返回的是新对象,原对象仍处于异常完成状态。
- 签名是
CompletableFuture<t> exceptionally(Function<throwable extends t> fn)</throwable></t> - 参数函数接收原始异常,必须返回与原始 CompletableFuture 相同类型的值
- 如果 exceptionally 内部也抛异常,新 CompletableFuture 就会以该新异常完成
基本用法示例
比如调用一个可能失败的远程接口:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) throw new RuntimeException("网络超时");
return "success";
}).exceptionally(throwable -> {
System.err.println("捕获异常: " + throwable.getMessage());
return "fallback result"; // 返回默认值,类型必须是 String
});
// 后续可正常 join 或 thenApply
String result = future.join(); // 不会抛异常,总是有值
和 handle、whenComplete 的区别
三者都可用于异常处理,但语义不同:
立即学习“Java免费学习笔记(深入)”;
- exceptionally:只在异常时执行,且必须返回结果(补救值),适合“降级”场景
- handle:无论成功或异常都会执行,接收 (result, throwable) 两个参数,更灵活,可统一处理
- whenComplete:也总是执行,但不改变结果值,只做副作用(如打日志、清理资源),不能返回新值
常见误用提醒
不要在 exceptionally 中试图“恢复”原始 future 的状态——它做不到。它只是生成替代结果。如果需要重试,应配合 supplyAsync + 循环逻辑,或用第三方库(如 resilience4j)。
- 别写
exceptionally(e -> { throw new RuntimeException("包装异常"); }),这会让调用方再次遇到异常 - 别忽略异常类型判断:若只想处理特定异常(如 IOException),需在函数内做 instanceof 判断,其他异常可 rethrow 或继续 fallback
- 避免在 exceptionally 中执行耗时或阻塞操作,否则会拖慢整个链路

















