Java 26正式发布!这3个新特性,让代码量直接减半
写了9年Java,我以为自己已经见惯了"版本更新",直到Java 26发布------这次,我是真的被惊艳到了。
大家好,我是卷毛。
3月份Java 26正式发布的时候,我正在重构一个老项目。随手升级了JDK试了试新特性,结果一个下午,干掉了项目里将近40%的样板代码。
今天这篇,不讲虚的,只聊我实际用下来真正提升效率的3个新特性。每个都附带代码对比,看完你就能直接用。
一、结构化并发正式转正:告别回调地狱
之前用CompletableFuture写并发,代码是这样的:
java
// Java 21 之前的写法 ------ 嵌套地狱
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> userService.findById(userId));
CompletableFuture<Order> orderFuture = CompletableFuture.supplyAsync(() -> orderService.findByUserId(userId));
userFuture.thenCombine(orderFuture, (user, order) -> {
CompletableFuture<List<Coupon>> couponFuture = CompletableFuture.supplyAsync(
() -> couponService.findByUser(user.getId())
);
couponFuture.thenAccept(coupons -> {
// 终于拿到所有数据了...但已经嵌套3层了
System.out.println(buildResult(user, order, coupons));
});
});
Java 26结构化并发正式转正后:
java
// Java 26 ------ 优雅到想哭
try (var scope = StructuredTaskScope.open()) {
var userTask = scope.fork(() -> userService.findById(userId));
var orderTask = scope.fork(() -> orderService.findByUserId(userId));
var couponTask = scope.fork(() -> couponService.findByUser(userId));
scope.join(); // 等所有任务完成
// 直接拿结果,没有嵌套,没有回调
return buildResult(userTask.get(), orderTask.get(), couponTask.get());
}
实际收益:我们项目里有个聚合接口,之前用CompletableFuture写了60多行,改完之后28行。更重要的是,异常处理终于清晰了------任何一个子任务失败,整个scope自动关闭,不用再手动写一堆exceptionally。
结构化并发的3个核心优势
| 特性 | CompletableFuture | StructuredTaskScope |
|---|---|---|
| 错误传播 | 手动处理 | 自动传播,父任务感知子任务失败 |
| 任务取消 | 手动管理 | 父任务取消,子任务自动取消 |
| 线程泄漏 | 可能泄漏 | scope关闭时自动清理 |
| 代码可读性 | 嵌套回调 | 扁平化,顺序读取 |
卷毛tips:如果你项目里有"聚合多个RPC调用"的场景,结构化并发是首选。配合虚拟线程,性能直接起飞。
二、Scoped Values正式上线:ThreadLocal的终结者
9年Java开发,ThreadLocal用了无数次,也踩了无数次坑。内存泄漏、线程池复用导致数据串台、InheritableThreadLocal在线程池下不生效......
Java 26终于把Scoped Values扶正了:
java
// 以前 ------ ThreadLocal的痛
private static final ThreadLocal<UserContext> CONTEXT = new ThreadLocal<>();
public void handleRequest(Request req) {
CONTEXT.set(buildContext(req));
try {
doBusiness();
} finally {
CONTEXT.remove(); // 忘了写就是内存泄漏
}
}
// Java 26 ------ Scoped Values的优雅
private static final ScopedValue<UserContext> CONTEXT = ScopedValue.newInstance();
public void handleRequest(Request req) {
ScopedValue.where(CONTEXT, buildContext(req)).run(() -> {
doBusiness();
// scope结束自动清理,不需要remove
// 虚拟线程中也能正确传递
});
}
我踩过的坑分享给你:
去年我们有个线上事故,就是ThreadLocal + 线程池导致的。用户A的请求处理完后,ThreadLocal没清理干净,线程被复用给用户B,结果用户B看到了用户A的数据。用Scoped Values后,这种问题从根本上就不存在了------值的作用域是代码块级别,出了scope就没了。
Scoped Values vs ThreadLocal 对比
java
// ❌ ThreadLocal:不可变、有泄漏风险、线程池下传递不可靠
ThreadLocal<String> userId = new ThreadLocal<>();
userId.set("user_001");
executor.submit(() -> {
// 线程池复用,可能拿到上一个任务的残留值
System.out.println(userId.get()); // 可能不是user_001!
});
// ✅ ScopedValue:不可变、无泄漏、虚拟线程友好
static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
ScopedValue.where(USER_ID, "user_001").run(() -> {
executor.submit(() -> {
// 虚拟线程中也能正确读取
System.out.println(USER_ID.get()); // 一定是user_001
});
});
三、模式匹配增强:switch终于像现代语言了
Java 26对模式匹配做了进一步增强,switch表达式现在支持guard条件和嵌套模式:
java
// 以前 ------ 一堆if-else
public String processShape(Shape shape) {
if (shape instanceof Circle c) {
if (c.radius() > 100) {
return "大圆";
} else {
return "小圆";
}
} else if (shape instanceof Rectangle r) {
if (r.width() == r.height()) {
return "正方形";
} else {
return "长方形";
}
} else if (shape instanceof Triangle t) {
if (t.angle() == 90) {
return "直角三角形";
}
return "普通三角形";
}
return "未知形状";
}
// Java 26 ------ 模式匹配 + guard条件
public String processShape(Shape shape) {
return switch (shape) {
case Circle(double r) when r > 100 -> "大圆";
case Circle(_) -> "小圆";
case Rectangle(double w, double h) when w == h -> "正方形";
case Rectangle(_, _) -> "长方形";
case Triangle(_, _, double angle) when angle == 90 -> "直角三角形";
case Triangle(_, _, _) -> "普通三角形";
};
}
代码量直接减半,而且可读性提升了好几个档次。
实战场景:处理支付回调
java
// 支付回调处理 ------ Java 26写法
public PaymentResult handleCallback(Callback callback) {
return switch (callback) {
case WechatCallback(var orderId, var amount, var status)
when status == Status.SUCCESS ->
paymentService.confirm(orderId, amount);
case WechatCallback(var orderId, _, var status)
when status == Status.FAILED ->
paymentService.fail(orderId, "微信支付失败");
case AlipayCallback(var orderId, var amount, var tradeNo)
when tradeNo != null ->
paymentService.confirm(orderId, amount);
case AlipayCallback(var orderId, _, null) ->
paymentService.pending(orderId);
case RefundCallback(var orderId, var refundAmount) ->
refundService.process(orderId, refundAmount);
};
}
升级建议
如果你的项目还在Java 17甚至Java 8,我的建议是:
- 先升级到Java 21(LTS),这是当前最稳定的LTS版本,虚拟线程、模式匹配初版都有了
- 在Java 21上跑稳之后,再评估是否升级到Java 26,主要看你是否需要结构化并发和Scoped Values
- 不要直接从Java 8跳到Java 26,中间的API变更太多,建议分步走
升级过程中最容易踩的坑:
sun.misc.Unsafe相关API被移除(很多老库依赖)- 反射访问内部API需要额外参数
--add-opens - 序列化相关行为有变化
写在最后
Java 26这次更新,说实话,让我这个写了9年Java的老兵重新燃起了热情。结构化并发解决了异步编程的可读性,Scoped Values解决了线程上下文传递的顽疾,模式匹配让代码真正变得优雅。
Java没有死,它只是在不紧不慢地变好。
📌 我是卷毛,9年Java开发,持续分享Java技术干货。
如果觉得这篇文章对你有帮助,点个关注不迷路,后续会持续更新Java新特性实战系列。
有问题欢迎在评论区交流,我会一一回复!
关注《卷毛的技术笔记》,一起卷出技术力。 🔥