Java 21 新特性详解

Java 21 于 2023 年 9 月正式发布,是继 Java 17 之后的下一个长期支持版本(LTS)。本文将带你全面了解 Java 21 带来的重要新特性及其实际应用。


一、虚拟线程(Virtual Threads)—— JEP 444

虚拟线程是 Java 21 中最重磅的特性,它彻底改变了 Java 的并发编程模型。

什么是虚拟线程?

传统 Java 线程(平台线程)与操作系统线程是 1:1 映射的,创建和切换成本高。虚拟线程由 JVM 管理,是轻量级线程,可以创建数百万个而不会耗尽系统资源。

代码对比

传统方式:

ExecutorService executor = Executors.newFixedThreadPool(200);
for (int i = 0; i < 10000; i++) {
    executor.submit(() -> {
        // 受限于线程池大小,大量任务需要排队
        var result = blockingHttpCall();
        return process(result);
    });
}

虚拟线程方式:

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 100_000; i++) {
        executor.submit(() -> {
            // 每个任务一个虚拟线程,轻松支持十万级并发
            var result = blockingHttpCall();
            return process(result);
        });
    }
}

核心优势

  • 轻量级:一个虚拟线程仅需几 KB 内存,可轻松创建百万级线程
  • 兼容性:现有阻塞式代码无需修改即可使用
  • 简化模型:告别复杂的响应式编程,回归简单的同步写法
  • 高效调度:JVM 自动将虚拟线程调度到少量平台线程上执行

使用建议

// 方式一:直接创建
Thread.startVirtualThread(() -> System.out.println("Hello from virtual thread!"));

// 方式二:使用 Builder
Thread.ofVirtual().name("my-vt-", 0).start(() -> {
    System.out.println(Thread.currentThread().getName());
});

// 方式三:使用 ExecutorService(推荐)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> doWork());
}

二、switch 模式匹配(Pattern Matching for switch)—— JEP 441

模式匹配让 switch 表达式更加灵活和安全,告别冗长的 instanceof 链。

基本用法

// 旧写法
static String format(Object obj) {
    if (obj instanceof Integer i) {
        return String.format("int %d", i);
    } else if (obj instanceof Long l) {
        return String.format("long %d", l);
    } else if (obj instanceof Double d) {
        return String.format("double %f", d);
    } else if (obj instanceof String s) {
        return String.format("String %s", s);
    } else {
        return obj.toString();
    }
}

// Java 21 新写法
static String format(Object obj) {
    return switch (obj) {
        case Integer i  -> String.format("int %d", i);
        case Long l     -> String.format("long %d", l);
        case Double d   -> String.format("double %f", d);
        case String s   -> String.format("String %s", s);
        case null       -> "null";
        default         -> obj.toString();
    };
}

守卫模式(Guarded Patterns)

static void test(Object obj) {
    switch (obj) {
        case String s when s.length() > 10 -> System.out.println("长字符串: " + s);
        case String s                      -> System.out.println("短字符串: " + s);
        case Integer i when i > 0          -> System.out.println("正整数: " + i);
        case Integer i                     -> System.out.println("非正整数: " + i);
        default                            -> System.out.println("其他类型");
    }
}

密封类(Sealed Classes)配合使用

sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double a, double b, double c) implements Shape {}

static double area(Shape shape) {
    return switch (shape) {
        case Circle c    -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Triangle t  -> {
            double s = (t.a() + t.b() + t.c()) / 2;
            yield Math.sqrt(s * (s - t.a()) * (s - t.b()) * (s - t.c()));
        }
        // 无需 default,编译器知道所有子类已被覆盖
    };
}

三、Record 模式(Record Patterns)—— JEP 440

Record 模式允许直接解构 Record 对象,使数据处理更加简洁。

基本解构

record Point(int x, int y) {}
record Line(Point start, Point end) {}

// 旧写法
static void printX(Object obj) {
    if (obj instanceof Point p) {
        System.out.println(p.x());
    }
}

// Java 21 新写法:直接解构
static void printX(Object obj) {
    if (obj instanceof Point(var x, var y)) {
        System.out.println("x = " + x + ", y = " + y);
    }
}

// 嵌套解构
static void printLine(Object obj) {
    if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
        System.out.printf("从 (%d,%d) 到 (%d,%d)%n", x1, y1, x2, y2);
    }
}

在 switch 中使用

static String describe(Object obj) {
    return switch (obj) {
        case Point(0, 0)               -> "原点";
        case Point(var x, 0)           -> "x 轴上的点: " + x;
        case Point(0, var y)           -> "y 轴上的点: " + y;
        case Point(var x, var y)       -> String.format("点 (%d, %d)", x, y);
        case Line(Point(var x1, var y1), Point(var x2, var y2)) ->
            String.format("线段 (%d,%d)->(%d,%d)", x1, y1, x2, y2);
        default                        -> "未知形状";
    };
}

四、序列化集合(Sequenced Collections)—— JEP 431

Java 21 引入了 SequencedCollection 接口,统一了有序集合的操作方式。

核心接口

// 新增的接口层次
SequencedCollection<E>    // List, Deque 等
SequencedSet<E>           // LinkedHashSet, TreeSet 等
SequencedMap<K, V>        // LinkedHashMap, TreeMap 等

常用方法

SequencedCollection<String> list = new ArrayList<>(List.of("A", "B", "C"));

list.addFirst("X");        // [X, A, B, C]
list.addLast("Y");         // [X, A, B, C, Y]
list.getFirst();           // X
list.getLast();            // Y
list.reversed();           // [Y, C, B, A, X]

// Map 也支持了
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.putFirst("first", 1);
map.putLast("last", 2);
map.firstEntry();          // first=1
map.lastEntry();           // last=2
map.reversed();            // 反向视图

实际价值

// 以前获取最后一个元素很麻烦
List<String> list = getList();
String last = list.get(list.size() - 1);  // 丑陋

// 现在一行搞定
String last = list.getLast();

五、未命名模式和变量(Unnamed Patterns & Variables)—— JEP 443

用下划线 _ 表示不需要使用的变量,提升代码可读性。

// 旧写法:不得不给变量命名
if (obj instanceof Point(var x, var y)) {
    System.out.println(x);  // y 未使用但必须声明
}

// 新写法:用 _ 忽略不需要的变量
if (obj instanceof Point(var x, _)) {
    System.out.println(x);  // 更清晰地表达意图
}

// switch 中使用
switch (obj) {
    case Line(Point(var x, _), _) -> System.out.println("x 坐标: " + x);
    default -> {}
}

// lambda 中使用
map.computeIfAbsent(key, _ -> new ArrayList<>());

// for 循环中使用
for (var _ : iterations) {
    doSomethingRepeated();
}

六、分代 ZGC(Generational ZGC)—— JEP 439

ZGC 升级为分代收集,显著提升性能。

核心改进

  • 分代管理:区分年轻对象和老年代对象
  • 更高吞吐量:分代 ZGC 的吞吐量比非分代提升约 10-20%
  • 更低延迟:最大暂停时间保持在亚毫秒级
  • 更低内存占用:减少堆内存的使用量

启用方式

# Java 21 中 ZGC 默认就是分代的
java -XX:+UseZGC MyApplication

# 如果需要非分代模式(兼容性)
java -XX:+UseZGC -XX:-ZGenerational MyApplication

七、其他值得关注的特性

未命名类和实例主方法(JEP 447)—— 预览特性

降低 Java 入门门槛,适合初学者:

// 不需要 class 声明,不需要 public static void main
void main() {
    System.out.println("Hello, Java 21!");
}

字符串模板(JEP 430)—— 预览特性

String name = "World";
int age = 25;

// 使用 STR 模板处理器
String msg = STR."Hello, \{name}! You are \{age} years old.";
// 结果: "Hello, World! You are 25 years old."

作用域值(Scoped Values)—— 预览特性

ThreadLocal 更安全、更高效的线程本地变量:

private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

// 设置值
ScopedValue.runWhere(CURRENT_USER, user, () -> {
    // 在此作用域内可以访问 CURRENT_USER.get()
    handleRequest();
});

八、总结

特性 状态 影响程度 适用场景
虚拟线程 正式 ⭐⭐⭐⭐⭐ 高并发、I/O 密集型应用
switch 模式匹配 正式 ⭐⭐⭐⭐ 复杂条件分支逻辑
Record 模式 正式 ⭐⭐⭐⭐ 数据处理、解析
序列化集合 正式 ⭐⭐⭐ 日常集合操作
未命名变量 正式 ⭐⭐⭐ 代码可读性优化
分代 ZGC 正式 ⭐⭐⭐⭐ 低延迟应用

Java 21 是一个里程碑式的 LTS 版本。 虚拟线程让 Java 在高并发场景下重获竞争力,模式匹配让代码更加简洁和类型安全,分代 ZGC 进一步巩固了 Java 在低延迟领域的地位。


posted @ 2026-05-29 11:07  World-Peace  阅读(42)  评论(0)    收藏  举报