Java 26正式发布!这3个新特性,让代码量直接减半

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,我的建议是:

  1. 先升级到Java 21(LTS),这是当前最稳定的LTS版本,虚拟线程、模式匹配初版都有了
  2. 在Java 21上跑稳之后,再评估是否升级到Java 26,主要看你是否需要结构化并发和Scoped Values
  3. 不要直接从Java 8跳到Java 26,中间的API变更太多,建议分步走

升级过程中最容易踩的坑:

  • sun.misc.Unsafe 相关API被移除(很多老库依赖)
  • 反射访问内部API需要额外参数--add-opens
  • 序列化相关行为有变化

写在最后

Java 26这次更新,说实话,让我这个写了9年Java的老兵重新燃起了热情。结构化并发解决了异步编程的可读性,Scoped Values解决了线程上下文传递的顽疾,模式匹配让代码真正变得优雅。

Java没有死,它只是在不紧不慢地变好。


📌 我是卷毛,9年Java开发,持续分享Java技术干货。

如果觉得这篇文章对你有帮助,点个关注不迷路,后续会持续更新Java新特性实战系列。

有问题欢迎在评论区交流,我会一一回复!

关注《卷毛的技术笔记》,一起卷出技术力。 🔥

相关推荐
小旭Coding2 小时前
卧靠!Go 传给前端的 int64 竟然变成了这个?
后端
用户298698530142 小时前
Word 文档文本查找与替换的 Java 实现方案
java·后端
阿哉2 小时前
Nacos 服务发现源码:藏在背后的两套事件机制,90%的人只讲了一半
java
kunge20132 小时前
深度剖析Claude Code 的CLAUDE.md加载逻辑
后端·vibecoding
米沙AI2 小时前
MSYS2 快速使用版本
后端
Csvn2 小时前
Docker 进阶 — 网络模型、数据持久化与多阶段构建
后端
用户4279254051712 小时前
《微博开放平台官方CLI开源了:70+API一行搞定,AI Agent原生支持》
后端
Csvn2 小时前
文本处理三剑客 — grep、sed、awk 实战精讲
后端