跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

JDK 21 核心新特性深度解析与实践

综述由AI生成JDK 21 作为 Oracle 发布的第四个长期支持版本,引入了虚拟线程、模式匹配 for switch、记录模式、密封类、分代 ZGC 及序列化集合六大正式特性,显著提升了开发效率与程序性能。此外,字符串模板、结构化并发和向量 API 等预览特性也为未来开发提供了更多可能。通过实际代码示例,详细演示了各特性的用法、适用场景及注意事项,帮助开发者快速掌握并应用到生产环境中。

机器人发布于 2026/3/29更新于 2026/6/915 浏览

JDK 21 是 Oracle 发布的继 JDK8、JDK11、JDK17 之后的第四个长期支持版(LTS),于 2023 年 9 月 19 日正式发布。它共包含 15 个新特性,涵盖语言增强、性能优化、API 扩展等多个维度,极大提升了开发效率、程序性能和代码安全性。

注意:以下示例均基于 JDK 21 环境编写,运行前请确保已安装 JDK 21 并配置好环境变量;涉及预览特性的代码,需在编译和运行时添加 --enable-preview 参数(具体用法见对应特性说明)。

一、核心正式特性

1. 虚拟线程(Virtual Threads,JEP 444)—— 轻量级高并发利器

1.1 特性说明

虚拟线程是 JDK 21 最受关注的核心特性,它是由 JVM 管理的轻量级线程,而非操作系统内核线程,彻底解决了传统平台线程(Platform Thread)资源占用高、上下文切换开销大的问题。虚拟线程的内存占用极低(每个仅需几 KB),可轻松创建数百万个并发任务,尤其适合 I/O 密集型场景(如接口调用、数据库操作、文件读写等),能极大提升程序吞吐量,且无需修改现有线程相关代码,上手成本极低。

核心优势包括:

  • 轻量级:内存占用远低于平台线程,单个 JVM 可创建数百万个虚拟线程。
  • 低开销:上下文切换由 JVM 管理,无需操作系统内核参与,开销极小。
  • 易迁移:现有基于 Runnable、Callable 的代码可直接迁移,无需修改业务逻辑。
  • 结构化并发:配合 Executors.newVirtualThreadPerTaskExecutor(),简化并发任务管理。
  • 调度优化:虚拟线程阻塞时会自动切换到其他正在运行的线程,充分利用 CPU 时间,避免资源闲置。
  • 无规模限制:不受操作系统对线程数量的限制,理论上支持无限量创建(受限于内存)。
1.2 代码样例(推荐第三种方式)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * 虚拟线程 4 种创建方式演示
 */
public class VirtualThreadsDemo {
    public static void main(String[] args) throws InterruptedException {
        // 方式 1:通过 Thread.startVirtualThread() 直接创建(最简单)
        Thread.startVirtualThread(() -> {
            System.out.println("方式 1:虚拟线程运行中,线程类型:" + Thread.currentThread().getClass().getSimpleName());
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("方式 1:虚拟线程执行完毕");
        });

        // 方式 2:通过 Thread.Builder 构建(可设置线程名称)
        Thread virtualThread = Thread.ofVirtual().name("virtual-thread-2")
                .unstarted(() -> {
                    System.out.println("方式 2:虚拟线程运行中,线程名称:" + Thread.currentThread().getName());
                    try {
                        TimeUnit.MILLISECONDS.sleep(150);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("方式 2:虚拟线程执行完毕");
                });
        virtualThread.start();

        // 方式 3:通过线程池创建(推荐,适合批量管理并发任务)
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 5; i++) {
                int taskId = i;
                executor.submit(() -> {
                    System.out.println("方式 3:虚拟线程任务" + taskId + "运行中,线程 ID:" + Thread.currentThread().threadId());
                    try {
                        TimeUnit.MILLISECONDS.sleep(200);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("方式 3:虚拟线程任务" + taskId + "执行完毕");
                });
            }
        }

        // 方式 4:通过 ThreadFactory 创建(灵活定制线程属性)
        ThreadFactory factory = Thread.ofVirtual().name("custom-virtual-thread-", 0).factory();
        Thread customThread = factory.newThread(() -> {
            System.out.println("方式 4:自定义虚拟线程运行中,线程名称:" + Thread.currentThread().getName());
            try {
                TimeUnit.MILLISECONDS.sleep(250);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("方式 4:自定义虚拟线程执行完毕");
        });
        customThread.start();

        // 等待所有虚拟线程执行完毕(主线程阻塞,避免程序提前退出)
        TimeUnit.SECONDS.sleep(1);
        System.out.println("所有虚拟线程执行完毕,主线程退出");
    }
}
1.3 运行结果(关键信息)
方式 1:虚拟线程运行中,线程类型:VirtualThread
方式 2:虚拟线程运行中,线程名称:virtual-thread-2
方式 3:虚拟线程任务 3 运行中,线程 ID:40
方式 3:虚拟线程任务 2 运行中,线程 ID:39
方式 3:虚拟线程任务 4 运行中,线程 ID:41
方式 3:虚拟线程任务 0 运行中,线程 ID:37
方式 3:虚拟线程任务 1 运行中,线程 ID:38
方式 1:虚拟线程执行完毕
方式 2:虚拟线程执行完毕
方式 3:虚拟线程任务 0 执行完毕
方式 3:虚拟线程任务 2 执行完毕
方式 3:虚拟线程任务 4 执行完毕
方式 3:虚拟线程任务 3 执行完毕
方式 3:虚拟线程任务 1 执行完毕
方式 4:自定义虚拟线程运行中,线程名称:custom-virtual-thread-0
方式 4:自定义虚拟线程执行完毕
所有虚拟线程执行完毕,主线程退出
1.4 注意事项
  • 虚拟线程适合 I/O 密集型场景,不适合 CPU 密集型场景(CPU 密集型任务建议使用平台线程,避免 JVM 调度开销)。
  • 虚拟线程不能调用 Thread.setDaemon()、Thread.suspend()、Thread.resume() 方法,调用会抛出 UnsupportedOperationException。
  • 通过 Executors.newVirtualThreadPerTaskExecutor() 创建的线程池,无需手动调用 shutdown(),try-with-resources 会自动关闭。
  • 虚拟线程的优先级设置仅对 JVM 调度有效,不影响操作系统内核线程的优先级。
  • 虚拟线程与平台线程完全兼容,可通过 Thread.isVirtual() 方法判断线程类型。

2. 模式匹配 for switch(Pattern Matching for switch,JEP 441)—— 简化分支判断

2.1 特性说明

JDK 21 正式定稿了 switch 模式匹配功能,该特性经过 4 轮预览迭代(JDK17-JDK20),最终成为正式特性。它扩展了 switch 的能力,允许 case 标签使用模式(而非仅常量),支持类型匹配、null 匹配、记录模式、守卫模式等,彻底解决了传统 switch 只能匹配常量、类型判断繁琐、空值处理麻烦的问题,让分支逻辑更简洁、更安全、更易维护。

核心增强点:

  • 支持类型模式:case 可直接匹配指定类型,无需手动强转。
  • 支持 null 匹配:专门的 case null,无需在 switch 外部判断空值。
  • 支持 guarded case(条件守卫):通过 when 子句添加额外判断条件。
  • 支持穷尽性检查:结合密封类使用时,编译器会检查是否覆盖所有可能的情况,避免遗漏。
  • 改进类型推断:编译器能更好地推断 switch 表达式的返回类型,减少显式声明。
2.2 代码样例(5 种常见场景)
import java.util.Objects;

/**
 * switch 模式匹配 5 种常见场景演示
 * 要求:Java 21+ 版本运行(所有特性均为正式特性)
 */
public class SwitchPatternMatchingDemo {
    public static void main(String[] args) {
        // 场景 1:类型模式匹配(替代 instanceof + 强转)
        Object obj1 = "JDK21";
        String typeResult = switch (obj1) {
            case String s -> "字符串类型,长度:" + s.length();
            case Integer i -> "整数类型,值:" + i;
            case Double d -> "浮点数类型,值:" + d;
            default -> "未知类型";
        };
        System.out.println("场景 1 结果:" + typeResult);

        // 场景 2:null 匹配(无需外部判断空值)
        String str = null;
        System.out.print("场景 2 结果:");
        switch (str) {
            case null -> System.out.println("字符串为 null");
            case "JDK" -> System.out.println("字符串是 JDK");
            default -> System.out.println("字符串:" + str);
        }

        // 场景 3:条件守卫(统一使用 when 子句)
        Object obj2 = 42;
        System.out.print("场景 3 结果:");
        switch (obj2) {
            case Integer i when i < 18 -> System.out.println("未成年,年龄:" + i);
            case Integer i when i >= 18 && i <= 60 -> System.out.println("成年,年龄:" + i);
            case Integer i -> System.out.println("老年,年龄:" + i);
            case String s when s.length() > 5 -> System.out.println("字符串长度大于 5:" + s);
            default -> System.out.println("未知数据");
        }

        // 场景 4:结合密封类(穷尽性检查,编译器自动校验)
        Shape shape = new Circle(5.0);
        double area = switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Triangle t -> (t.base() * t.height()) / 2.0;
        };
        System.out.println("场景 4 结果:图形面积:" + String.format("%.2f", area));

        // 场景 5:记录模式匹配(直接解构记录属性)
        Person person = new Person("Alice", 30);
        String personInfo = switch (person) {
            case Person(String name, int age) when age < 18 -> "未成年人:" + name;
            case Person(String name, int age) -> "成年人:" + name + "(" + age + "岁)";
        };
        System.out.println("场景 5 结果:" + personInfo);
    }

    // 密封类:限制子类只能是指定的几个类
    public sealed interface Shape permits Circle, Rectangle, Triangle {}
    public record Circle(double radius) implements Shape {}
    public record Rectangle(double width, double height) implements Shape {}
    public record Triangle(double base, double height) implements Shape {}

    // 记录类:用于演示记录模式匹配
    public record Person(String name, int age) {}
}
2.3 运行结果
场景 1 结果:字符串类型,长度:5
场景 2 结果:字符串为 null
场景 3 结果:成年,年龄:42
场景 4 结果:图形面积:78.54
场景 5 结果:成年人:Alice(30 岁)
2.4 注意事项
  • case 标签的顺序会影响匹配结果,更具体的模式(如带条件守卫的 case)应放在前面,避免被更宽泛的模式覆盖。
  • 当 switch 的表达式为 null 时,会优先匹配 case null,无需在 switch 前添加 Objects.nonNull() 判断。
  • 结合密封类使用时,若遗漏密封类的某个子类,编译器会直接报错(穷尽性检查),避免运行时异常。
  • 模式变量(如 case String s 中的 s)仅在当前 case 分支中有效,不可跨分支使用。
  • 支持两种条件守卫语法:when 子句(更优雅)和 && 逻辑判断(更灵活),可根据场景选择。

3. 记录模式(Record Patterns,JEP 440)—— 简化数据解构

3.1 特性说明

记录模式是 JDK 21 正式特性,它扩展了模式匹配的能力,允许直接解构记录(Record)的组件,无需手动调用 record 的 getter 方法,同时支持嵌套解构、部分解构,可轻松提取复杂对象的深层属性,让代码更简洁、更具可读性。该特性从 JDK19 开始预览,经过两轮优化后正式定稿,常与 switch 模式匹配配合使用,是处理不可变数据的最佳实践。

核心价值:

  • 简化记录解构:直接提取 record 的组件,无需调用 xxx() 方法。
  • 支持嵌套解构:可多层嵌套,提取复杂对象的深层属性。
  • 支持部分解构:无需关注所有组件,可忽略不需要的属性。
  • 与 switch/instanceof 无缝集成:实现复杂数据的快速判断和解构。
  • 编译时安全:通过编译时检查确保属性访问的正确性,避免类型错误。
3.2 代码样例(基础解构 + 嵌套解构 + 部分解构)
import java.time.LocalDate;

/**
 * 记录(Record)模式匹配 - 解构演示
 * 要求:Java 21+
 */
public class RecordPatternMatchingDemo {
    // 基础记录(无嵌套)
    public record Point(int x, int y) {}
    // 嵌套记录(包含 Point 记录)
    public record ColoredPoint(Point point, String color) {}
    // 多层嵌套记录(包含 ColoredPoint 记录)
    public record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
    // 复杂业务记录(包含嵌套记录和普通类型)
    public record Order(String orderId, LocalDate createTime, ColoredPoint deliveryAddress) {}

    public static void main(String[] args) {
        // 场景 1:基础记录解构(直接提取 Point 的 x 和 y)
        Point point = new Point(10, 20);
        if (point instanceof Point(int x, int y)) {
            System.out.println("场景 1:基础解构 - x=" + x + ", y=" + y);
        }

        // 场景 2:嵌套记录解构(提取 ColoredPoint 中的 Point 组件)
        ColoredPoint coloredPoint = new ColoredPoint(new Point(5, 8), "red");
        if (coloredPoint instanceof ColoredPoint(Point(int x, int y), String color)) {
            System.out.println("场景 2:嵌套解构 - x=" + x + ", y=" + y + ", color=" + color);
        }

        // 场景 3:多层嵌套解构(提取 Rectangle 中的深层 Point 组件)
        Rectangle rectangle = new Rectangle(
                new ColoredPoint(new Point(1, 2), "blue"),
                new ColoredPoint(new Point(3, 4), "blue")
        );
        if (rectangle instanceof Rectangle(
                ColoredPoint(Point(int x1, int y1), String color1),
                ColoredPoint(Point(int x2, int y2), String color2)
        )) {
            System.out.println("场景 3:多层嵌套解构 - 左上角(" + x1 + "," + y1 + "," + color1 + "), 右下角(" + x2 + "," + y2 + "," + color2 + ")");
        }

        // 场景 4:部分解构(使用 _ 表示不需要的组件)
        Order order = new Order("ORD-2024-001", LocalDate.now(), new ColoredPoint(new Point(100, 200), "green"));
        if (order instanceof Order(String orderId, _, ColoredPoint(Point(int x, int y), _))) {
            System.out.println("场景 4:部分解构 - 订单 ID:" + orderId + ", 配送坐标:(" + x + "," + y + ")");
        }

        // 场景 5:结合 switch 模式匹配
        Object obj = new ColoredPoint(new Point(3, 5), "green");
        String deconstructionResult = switch (obj) {
            case Point(int x, int y) -> "Point: (" + x + "," + y + ")";
            case ColoredPoint(Point(int x, int y), String color) -> "ColoredPoint: (" + x + "," + y + "), " + color;
            case Rectangle(ColoredPoint p1, ColoredPoint p2) -> "Rectangle: " + p1 + ", " + p2;
            case Order(String orderId, _, _) -> "Order: " + orderId;
            default -> "未知记录类型";
        };
        System.out.println("场景 5:switch 结合记录模式 - " + deconstructionResult);
    }
}
3.3 运行结果
场景 1:基础解构 - x=10, y=20
场景 2:嵌套解构 - x=5, y=8, color=red
场景 3:多层嵌套解构 - 左上角 (1,2,blue), 右下角 (3,4,blue)
场景 4:部分解构 - 订单 ID:ORD-2024-001, 配送坐标:(100,200)
场景 5:switch 结合记录模式 - ColoredPoint: (3,5), green
3.4 注意事项
  • 记录模式仅适用于 record 类,普通类无法使用(record 类是不可变的,组件具有固定的 getter 方法)。
  • 嵌套解构时,需保证每层的模式匹配正确,若某一层不匹配,则整个模式匹配失败。
  • 可使用 var 简化变量声明,例如 Point(var x, var y),编译器会自动推断变量类型。
  • 部分解构时,可使用下划线 _ 表示不需要的组件,提高代码可读性。
  • 记录模式可与 instanceof、switch 配合使用,尤其适合处理 DTO、VO 等不可变数据对象。

4. 密封类(Sealed Classes,JEP 409)—— 控制类继承

4.1 特性说明

密封类在 JDK 21 中正式定型,它允许开发者限制类或接口的继承/实现范围,明确指定哪些类可以继承它,避免不必要的继承和扩展,提升代码的可维护性和安全性。密封类从 JDK15 开始预览,经过多轮优化后正式成为标准特性,常与 switch 模式匹配配合使用,实现穷尽性检查,确保分支逻辑覆盖所有可能的子类。

核心规则:

  • 密封类使用 sealed 修饰,通过 permits 关键字指定允许继承的子类。
  • 密封类的子类必须使用以下三种修饰符之一:
    • final:不可再被继承。
    • sealed:可继续限制子类(需再次使用 permits 指定)。
    • non-sealed:开放继承,允许任意类继承。
  • 密封接口的实现类,也需遵循上述修饰符规则。
  • 密封类及其子类必须在同一个包下(若使用模块,需在同一个模块下)。
4.2 代码样例(密封类 + 密封接口 + 多层密封)
package org.example.model;

/**
 * 密封类 + 密封接口 + 多层密封 演示
 * 要求:Java 21+
 */
public class SealedClassDemo {
    public static void main(String[] args) {
        // 测试密封类(静态内部类可直接实例化)
        Animal cat = new Cat();
        Animal dog = new Dog();
        Animal puppy = new Puppy();
        Animal bird = new Bird();
        System.out.println("猫的叫声:" + cat.makeSound());
        System.out.println("狗的叫声:" + dog.makeSound());
        System.out.println("小狗的叫声:" + puppy.makeSound());
        System.out.println("鸟的叫声:" + bird.makeSound());

        // 测试密封接口
        Fruit apple = new Apple();
        Fruit banana = new Banana();
        Fruit orange = new Orange();
        System.out.println("苹果的颜色:" + apple.getColor());
        System.out.println("香蕉的颜色:" + banana.getColor());
        System.out.println("橙子的颜色:" + orange.getColor());

        // 测试穷尽性检查(switch + 密封类)
        printAnimalType(cat);
        printAnimalType(dog);
        printAnimalType(puppy);
        printAnimalType(bird);
    }

    // 密封 + 抽象类:仅允许 Cat、Dog、Bird 继承
    public static abstract sealed class Animal permits Cat, Dog, Bird {
        public abstract String makeSound();
    }

    // final 子类:不可再继承
    public static final class Cat extends Animal {
        @Override
        public String makeSound() {
            return "喵喵喵";
        }
    }

    // sealed 子类:可继续限制子类(仅允许 Puppy 继承)
    public static sealed class Dog extends Animal permits Puppy {
        @Override
        public String makeSound() {
            return "汪汪汪";
        }
    }

    // Dog 的子类,final 修饰
    public static final class Puppy extends Dog {
        @Override
        public String makeSound() {
            return "呜呜呜(小狗叫)";
        }
    }

    // non-sealed 子类:开放继承,允许任意类继承
    public static non-sealed class Bird extends Animal {
        @Override
        public String makeSound() {
            return "叽叽喳喳";
        }
    }

    // 密封接口:仅允许 Apple、Banana、Orange 实现
    public static sealed interface Fruit permits Apple, Banana, Orange {
        String getColor();
    }

    public static final class Apple implements Fruit {
        @Override
        public String getColor() {
            return "红色";
        }
    }

    public static final class Banana implements Fruit {
        @Override
        public String getColor() {
            return "黄色";
        }
    }

    public static non-sealed class Orange implements Fruit {
        @Override
        public String getColor() {
            return "橙色";
        }
    }

    // 结合 switch 实现穷尽性检查
    private static void printAnimalType(Animal animal) {
        String type = switch (animal) {
            case Cat ignore -> "猫科动物";
            case Puppy ignore -> "犬科动物(小狗)";
            case Dog ignore -> "犬科动物(成年犬)";
            case Bird ignore -> "鸟类动物";
        };
        System.out.println("动物类型:" + type);
    }
}
4.3 运行结果
猫的叫声:喵喵喵
狗的叫声:汪汪汪
小狗的叫声:呜呜呜(小狗叫)
鸟的叫声:叽叽喳喳
苹果的颜色:红色
香蕉的颜色:黄色
橙子的颜色:橙色
动物类型:猫科动物
动物类型:犬科动物(成年犬)
动物类型:犬科动物(小狗)
动物类型:鸟类动物
4.4 注意事项
  • 密封类的 permits 关键字后,必须列出所有允许继承的子类,不能遗漏。
  • 密封类不能是 final 的(final 类不可继承,与密封类的设计初衷冲突)。
  • 非密封子类(non-sealed)可以被任意类继承,打破密封类的限制,适合需要灵活扩展的场景。
  • 密封类与 switch 模式匹配配合时,编译器会自动检查是否覆盖所有子类,避免遗漏分支。
  • 密封接口的实现类同样需要遵循修饰符规则,确保接口的实现范围可控。

5. 分代 ZGC(Generational ZGC,JEP 439)—— 提升 GC 性能

5.1 特性说明

ZGC 是 Java 推出的低延迟垃圾收集器,JDK 21 正式将分代 ZGC 从实验特性转为正式特性(JEP 439 Final 状态),其核心是将堆内存分为年轻代和老年代,针对不同代的对象采用差异化收集策略:

  • 年轻代对象生命周期短,采用频繁、快速的收集策略;
  • 老年代对象生命周期长,采用低频率、高效率的收集策略。

这一优化极大提升了垃圾收集的吞吐量和响应速度,尤其适合大堆内存场景(如 10GB 以上堆内存)。

核心优势:

  • 低延迟:GC 停顿时间控制在毫秒级甚至亚毫秒级,几乎不影响程序正常运行;
  • 高吞吐量:分代收集减少了不必要的垃圾扫描范围,提升垃圾收集效率;
  • 大堆支持:64 位系统下最大支持 16TB 堆内存(设计上限),可轻松应对大内存应用场景;
  • 并发处理:年轻代和老年代的核心垃圾收集流程均为并发执行,应用线程无需长时间阻塞;
  • 动态内存布局:可根据应用实际内存需求自动调整年轻代 / 老年代的大小;
  • 无缝迁移:无需修改业务代码,仅通过 JVM 参数即可启用,无侵入性。

官方规划:OpenJDK 计划在 JDK 25 及后续版本中将分代 ZGC 设为默认垃圾收集器,后续会逐步移除非分代 ZGC 实现,ZGenerational 选项也将不再需要手动配置。

5.2 启用方式(JVM 参数)
# 启用分代 ZGC(JDK21 正式特性,无需解锁实验性选项)
-XX:+UseZGC
-XX:+ZGenerational

# 可选参数(优化大堆场景)
-XX:ZHeapSize=10G          # 设置堆内存总大小为 10G
-XX:MaxGCPauseMillis=10    # 建议最大 GC 停顿时间(ZGC 会尽力满足,非强制)
-XX:ZYoungGenSize=2G       # 设置年轻代初始大小为 2G(默认自动动态调整,无需手动配置)
5.3 实际应用示例
# 运行 Spring Boot 应用并启用分代 ZGC(JDK21+)
java -XX:+UseZGC -XX:+ZGenerational -XX:ZHeapSize=20G -jar your-app.jar

# 进阶配置(指定年轻代大小 + 停顿时间目标)
java -XX:+UseZGC -XX:+ZGenerational -XX:ZHeapSize=32G -XX:ZYoungGenSize=4G -XX:MaxGCPauseMillis=5 -jar your-app.jar
5.4 注意事项
  • 版本要求:分代 ZGC 仅在 JDK 21 及以上版本支持,JDK 21 之前的 ZGC 无分代收集能力;
  • 排他性:启用分代 ZGC 后,不可同时启用 G1、Shenandoah 等其他垃圾收集器,避免 JVM 参数冲突;
  • 场景适配:适合大堆内存场景(堆内存 ≥ 4GB),小堆内存场景(<4GB)使用 G1 收集器性价比更高;
  • 参数原则:分代 ZGC 会自动动态调整年轻代 / 老年代大小,除非有明确的性能调优需求,否则无需手动配置 ZYoungGenSize;
  • 技术依赖:依赖染色指针(Colored Pointers)和读屏障(Load Barriers)技术,64 位主流操作系统 / 虚拟机环境默认已启用,无需额外配置;
  • 堆上限:ZGC 设计最大堆大小为 16TB(JDK 21),实际可用上限受操作系统、硬件物理内存限制,无法突破 16TB。

6. 序列化集合(Sequenced Collections,JEP 431)—— 有序集合标准化

6.1 特性说明

JDK 21 正式引入 Sequenced Collections(序列化集合,又称有序集合),这是一种具有确定出现顺序(encounter order)的集合,无论遍历多少次,元素的出现顺序始终固定。它提供了统一的接口和方法来处理集合的首尾元素、反向视图等,解决了以往不同集合(List、Set、Map)处理顺序操作的 API 不一致问题。

核心优势:

  • 统一接口:提供 SequencedCollection、SequencedSet、SequencedMap 三大核心接口,标准化顺序操作。
  • 有序性保证:元素插入顺序与遍历顺序一致,支持首尾元素快速访问。
  • 可扩展性:设计考虑了大量数据的并发访问,适合高并发场景。
  • 反向视图:通过 reversed() 方法快速获取反向顺序的集合视图,无需手动反转。

接口关系:

  • SequencedCollection:继承 Collection,基础有序集合接口。
  • SequencedSet:继承 SequencedCollection 和 Set,有序不重复集合接口。
  • SequencedMap:继承 Map,有序键值对映射接口。
6.2 代码样例(三大接口完整演示)
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.SequencedCollection;
import java.util.SequencedMap;
import java.util.SequencedSet;

/**
 * 有序集合(Sequenced Collections)三大接口演示
 * 注意:使用 LinkedList(而非 ArrayList),因为其支持 addFirst()/addLast()
 */
public class SequencedCollectionsDemo {
    public static void main(String[] args) {
        // 场景 1:SequencedCollection 演示(以 LinkedList 为例,支持 addFirst/addLast)
        System.out.println("=== 场景 1:SequencedCollection 演示 ===");
        SequencedCollection<Integer> collection = new LinkedList<>();
        collection.add(1);
        collection.addFirst(0); // 添加到头部(LinkedList 支持,ArrayList 不支持)
        collection.addLast(2);  // 添加到尾部
        System.out.println("集合元素:" + collection);
        System.out.println("第一个元素:" + collection.getFirst());
        System.out.println("最后一个元素:" + collection.getLast());
        SequencedCollection<Integer> reversedCollection = collection.reversed();
        System.out.println("反向集合:" + reversedCollection);
        collection.removeFirst(); // 删除头部元素
        collection.removeLast();  // 删除尾部元素
        System.out.println("删除首尾后:" + collection);

        // 场景 2:SequencedSet 演示(以 LinkedHashSet 为例)
        System.out.println("\n=== 场景 2:SequencedSet 演示 ===");
        SequencedSet<Integer> set = new LinkedHashSet<>();
        set.add(3);
        set.add(1);
        set.add(2);
        set.addFirst(0); // 添加到头部
        set.addLast(4);  // 添加到尾部
        System.out.println("Set 元素(有序不重复):" + set);
        System.out.println("第一个元素:" + set.getFirst());
        System.out.println("最后一个元素:" + set.getLast());
        SequencedSet<Integer> reversedSet = set.reversed();
        System.out.println("反向 Set:" + reversedSet);

        // 场景 3:SequencedMap 演示(以 LinkedHashMap 为例)
        System.out.println("\n=== 场景 3:SequencedMap 演示 ===");
        SequencedMap<Integer, String> map = new LinkedHashMap<>();
        map.put(1, "Apple");
        map.put(2, "Banana");
        map.put(3, "Orange");
        map.putFirst(0, "Grape"); // 添加到头部
        map.putLast(4, "Mango");  // 添加到尾部
        System.out.println("Map 元素:" + map);
        System.out.println("第一个键值对:" + map.firstEntry());
        System.out.println("最后一个键值对:" + map.lastEntry());
        // 获取有序的 key、value、entry 集合
        System.out.println("有序 key 集合:" + map.sequencedKeySet());
        System.out.println("有序 value 集合:" + map.sequencedValues());
        System.out.println("有序 entry 集合:" + map.sequencedEntrySet());
        // 移除首尾键值对
        map.pollFirstEntry(); // 移除并返回第一个键值对
        map.pollLastEntry();  // 移除并返回最后一个键值对
        System.out.println("移除首尾后:" + map);
        // 反向 Map
        SequencedMap<Integer, String> reversedMap = map.reversed();
        System.out.println("反向 Map:" + reversedMap);
    }
}
6.3 运行结果
=== 场景 1:SequencedCollection 演示 ===
集合元素:[0, 1, 2]
第一个元素:0
最后一个元素:2
反向集合:[2, 1, 0]
删除首尾后:[1]
=== 场景 2:SequencedSet 演示 ===
Set 元素(有序不重复):[0, 3, 1, 2, 4]
第一个元素:0
最后一个元素:4
反向 Set:[4, 2, 1, 3, 0]
=== 场景 3:SequencedMap 演示 ===
Map 元素:{0=Grape, 1=Apple, 2=Banana, 3=Orange, 4=Mango}
第一个键值对:0=Grape
最后一个键值对:4=Mango
有序 key 集合:[0, 1, 2, 3, 4]
有序 value 集合:[Grape, Apple, Banana, Orange, Mango]
有序 entry 集合:[0=Grape, 1=Apple, 2=Banana, 3=Orange, 4=Mango]
移除首尾后:{1=Apple, 2=Banana, 3=Orange}
反向 Map:{3=Orange, 2=Banana, 1=Apple}
6.4 注意事项
  • 实现类说明:
    • List 接口继承 SequencedCollection(LinkedList 重写 addFirst()/addLast() 可正常使用,ArrayList 的同名方法抛 UnsupportedOperationException);
    • Deque 接口继承 SequencedCollection(如 ConcurrentLinkedDeque、ArrayDeque);
    • LinkedHashSet、TreeSet(实现 NavigableSet)实现 SequencedSet;
    • LinkedHashMap、TreeMap(实现 NavigableMap)实现 SequencedMap。
  • 线程安全:SequencedCollections 接口本身不保证线程安全,需使用线程安全的实现类(如 ConcurrentLinkedDeque)或手动加锁;ConcurrentLinkedDeque 的线程安全基于 CAS 机制实现。
  • 反向视图:reversed() 方法返回的是原集合的反向视图(而非新集合),修改原集合会同步影响反向视图,反之亦然。
  • 空集合处理:调用空集合的 getFirst()、getLast() 方法会抛出 NoSuchElementException,需提前通过 isEmpty() 判断集合是否为空。
  • 与传统方法对比:
    • addFirst()/addLast() 是对 Deque 同名方法的标准化扩展(覆盖到所有 List),而非'替代';
    • sequencedKeySet() 是 Map.keySet() 的有序增强版(返回 SequencedSet),二者共存,并非'替代'。

二、预览特性(需启用 --enable-preview 参数)

预览特性是 JDK 中已实现但尚未正式定型的特性,可能在未来版本中优化或调整,使用时需在编译和运行时添加 --enable-preview 参数(具体用法见每个特性的说明)。JDK 21 中有多个实用预览特性,以下重点讲解最常用的 5 个。

1. 字符串模板(String Templates,JEP 430)—— 简化字符串拼接

1.1 特性说明

字符串模板是 JDK 21 的预览特性,类似 Python 的 f-string、JavaScript 的模板字符串,允许在字符串中直接嵌入表达式,无需使用 + 拼接或 String.format(),支持三种内置模板处理器,同时支持自定义处理器,解决了传统字符串拼接繁琐、易出错、可读性差的问题。

核心优势:

  • 简洁高效:直接嵌入表达式,无需拼接,代码更简洁。
  • 类型安全:表达式类型自动适配,无需手动转换。
  • 支持格式化:通过 FMT 处理器支持格式化说明符(如 %.2f、%-10s)。
  • 支持自定义:可实现 StringTemplate.Processor 接口创建自定义处理器。
  • 多行支持:支持多行字符串模板,保留换行和缩进格式。
  • 表达式灵活:支持变量、方法调用、复杂计算、注释等多种表达式形式。

内置模板处理器:

  • STR:基础处理器,自动执行字符串插值,将表达式替换为字符串值。
  • FMT:格式化处理器,支持格式化说明符(如 %.2f、%-10s)。
  • RAW:原始处理器,返回 StringTemplate 对象,不自动处理插值,用于自定义处理。
1.2 代码样例(完整用法演示)
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * 字符串模板(预览特性)完整用法演示
 * 编译命令:javac --enable-preview --release 21 StringTemplateDemo.java
 * 运行命令:java --enable-preview StringTemplateDemo
 */
public class StringTemplateDemo {
    // 测试方法:用于表达式调用
    private static String getName() {
        return "Java 开发者";
    }

    // 自定义模板处理器:转换为大写格式(兼容 null 参数)
    private static class UpperCaseProcessor implements StringTemplate.Processor<String, RuntimeException> {
        @Override
        public String process(StringTemplate st) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < st.fragments().size(); i++) {
                sb.append(st.fragments().get(i));
                if (i < st.arguments().size()) {
                    Object arg = st.arguments().get(i);
                    // 兼容 null 参数,避免空指针
                    sb.append(arg == null ? "null" : arg.toString().toUpperCase());
                }
            }
            return sb.toString();
        }
    }

    public static void main(String[] args) {
        // 基础信息
        String name = "Alice";
        int age = 30;
        double salary = 15000.50;
        String userInput = "<script>alert('攻击')</script>";

        // 场景 1:STR 处理器(基础插值)
        String info1 = STR."姓名:\{name},年龄:\{age},薪资:\{salary}";
        System.out.println("场景 1:STR 基础插值 - " + info1);

        // 场景 2:STR 处理器(表达式调用:变量、方法、计算)
        String info2 = STR."静态变量:\{name},方法调用:\{getName()},计算结果:\{10 + 20 == 30 ? "正确" : "错误"}";
        System.out.println("场景 2:STR 表达式调用 - " + info2);

        // 场景 3:FMT 处理器(格式化)
        String info3 = FMT."姓名:%-10s\{name},年龄:%03d\{age},薪资:%.2f\{salary}";
        System.out.println("场景 3:FMT 格式化 - " + info3);

        // 场景 4:RAW 处理器(原始模板,自定义处理)
        StringTemplate rawTemplate = RAW."姓名:\{name},年龄:\{age}";
        System.out.println("场景 4:RAW 原始文本 - " + rawTemplate.text());
        System.out.println("场景 4:RAW 插值后(STR 处理) - " + STR.process(rawTemplate));

        // 场景 5:多行字符串模板
        String info4 = STR."""
            个人信息:
            姓名:\{name}
            年龄:\{age}岁
            薪资:\{salary}元/月
            当前时间:\{
                // 多行表达式支持注释
                DateTimeFormatter.ofPattern("HH:mm:ss")
                    .format(LocalTime.now())
            }
            """;
        System.out.println("场景 5:多行模板 - " + info4);

        // 场景 6:自定义模板处理器(修正调用语法)
        UpperCaseProcessor upperProcessor = new UpperCaseProcessor();
        StringTemplate upperTemplate = STR."姓名:\{name},年龄:\{age}";
        String info5 = upperProcessor.process(upperTemplate);
        System.out.println("场景 6:自定义处理器(大写) - " + info5);

        // 场景 7:安全说明:STR/FMT 不自动转义,防注入需自定义处理器
        String unsafeInfo = STR."用户输入:\{userInput}";
        System.out.println("场景 7:未转义输入 - " + unsafeInfo);
    }
}
1.3 运行结果
场景 1:STR 基础插值 - 姓名:Alice,年龄:30,薪资:15000.5
场景 2:STR 表达式调用 - 静态变量:Alice,方法调用:Java 开发者,计算结果:正确
场景 3:FMT 格式化 - 姓名:Alice ,年龄:030,薪资:15000.50
场景 4:RAW 原始文本 - 姓名:\{name},年龄:\{age}
场景 4:RAW 插值后(STR 处理) - 姓名:Alice,年龄:30
场景 5:多行模板 - 个人信息:
姓名:Alice
年龄:30 岁
薪资:15000.5 元/月
当前时间:14:35:22
场景 6:自定义处理器(大写) - 姓名:ALICE,年龄:30
场景 7:未转义输入 - 用户输入:<script>alert('攻击')</script>
1.4 注意事项
  • 启用方式:必须在编译和运行时添加 --enable-preview --release 21 参数(JDK 22/23 同理),否则会提示'字符串模板是预览特性,需启用预览模式';
  • 安全说明:STR 和 FMT 处理器不会自动转义特殊字符(如 <>/"'),处理用户输入时需自定义处理器实现 XSS / 注入防护;
  • 多行模板:使用 """ 包裹,保留换行和缩进格式,表达式嵌入方式与单行一致,多行表达式内可写注释;
  • 自定义处理器:
    • 实现 StringTemplate.Processor 接口的 process 方法,入参为 StringTemplate(包含模板文本片段 fragments() 和表达式参数 arguments());
    • 调用时需先通过 STR/RAW 创建 StringTemplate 对象,再传入处理器的 process() 方法;
    • 需处理参数为 null 的情况,避免空指针异常;
  • 表达式限制:模板中的表达式不能包含未声明的变量,表达式执行抛出的异常会直接传播,需手动捕获处理;
  • 版本说明:JDK 21/22/23 中 String Templates 均为预览特性,正式版尚未发布,生产环境慎用。

2. 结构化并发(Structured Concurrency,JEP 453)—— 简化并发任务管理

2.1 特性说明

结构化并发是 JDK 21 的预览特性(JDK 23 正式转正),旨在简化多线程并发任务的管理,解决传统并发编程中线程泄漏、任务间依赖混乱、异常处理繁琐等问题。它将一组相关的并发任务视为一个整体,统一管理生命周期(启动、执行、取消、终止),确保所有子任务完成后主线程再继续执行,同时支持异常统一捕获和任务取消扩散,让并发代码更具可读性、可维护性和安全性。

核心优势:

  • 生命周期统一:子任务与父任务绑定,父任务取消 / 终止时,所有子任务自动取消,避免线程泄漏;
  • 异常集中处理:子任务抛出的异常会被收集到 scope 中,可统一获取和处理,无需单独捕获;
  • 任务依赖清晰:通过结构化层级明确任务间的父子关系,避免并发任务逻辑混乱;
  • 与虚拟线程兼容:可灵活搭配虚拟线程使用,轻松管理大量并发子任务,提升高并发场景性能;
  • 取消扩散:父任务取消时,会自动中断所有未完成的子任务,避免资源浪费;
  • 自动资源管理:结合 try-with-resources 语法,确保 scope 自动关闭,杜绝线程泄漏。
2.2 代码样例(基础使用 + 异常处理 + 取消任务)
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 结构化并发(预览特性)演示
 * 编译命令:javac --enable-preview --release 21 StructuredConcurrencyDemo.java
 * 运行命令:java --enable-preview StructuredConcurrencyDemo
 */
public class StructuredConcurrencyDemo {
    public static void main(String[] args) throws InterruptedException {
        // 场景 1:基础结构化并发(父子任务绑定)
        System.out.println("=== 场景 1:基础结构化并发 ===");
        try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
            // 提交子任务 1(虚拟线程需显式指定线程工厂,默认平台线程)
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(100);
                return "子任务 1 执行完成";
            });
            // 提交子任务 2
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(200);
                return "子任务 2 执行完成";
            });
            // 等待子任务完成(ShutdownOnSuccess:任一成功则关闭其他任务)
            scope.join();
            // 获取成功完成的子任务结果(第一个成功的)
            System.out.println("成功结果:" + scope.result());
        }
        // try-with-resources 自动关闭 scope

        // 场景 2:异常处理(子任务抛出异常,父任务统一捕获)
        System.out.println("\n=== 场景 2:异常处理 ===");
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 子任务 3:抛出异常
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(50);
                System.out.println("子任务 3 执行中");
                throw new RuntimeException("子任务 3 执行失败");
            });
            // 子任务 4:因子任务 3 失败,会被提前终止,不会执行到打印
            scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(150);
                System.out.println("子任务 4 执行完成");
                return "子任务 4 成功";
            });
            try {
                scope.join(); // 等待,ShutdownOnFailure:任一失败则关闭
            } catch (ExecutionException e) {
                // 捕获封装的异常,实际异常在 scope.failures() 中
                System.out.println("捕获到执行异常:" + e.getMessage());
            }
            // 遍历所有子任务的异常(核心:从 failures() 获取)
            scope.failures().forEach(ex -> System.out.println("子任务异常:" + ex.getMessage()));
        }

        // 场景 3:手动取消任务
        System.out.println("\n=== 场景 3:手动取消任务 ===");
        try (var scope = new StructuredTaskScope<String>()) {
            // 子任务 5:耗时任务
            var future1 = scope.fork(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(1000);
                    return "子任务 5 执行完成";
                } catch (InterruptedException e) {
                    System.out.println("子任务 5 被取消");
                    return null;
                }
            });
            // 子任务 6:快速完成
            var future2 = scope.fork(() -> {
                TimeUnit.MILLISECONDS.sleep(100);
                return "子任务 6 执行完成";
            });
            // 等待 150ms,或所有子任务完成
            try {
                scope.joinUntil(System.currentTimeMillis() + 150);
                if (future2.isDone()) {
                    System.out.println("子任务 6 结果:" + future2.resultNow());
                    scope.shutdown(); // 触发子任务中断(子任务 5)
                }
            } catch (TimeoutException e) {
                System.out.println("等待超时");
            }
        }
    }
}
2.3 运行结果
=== 场景 1:基础结构化并发 ===
成功结果:子任务 1 执行完成
=== 场景 2:异常处理 ===
子任务 3 执行中
捕获到执行异常:java.lang.RuntimeException: 子任务 3 执行失败
子任务异常:子任务 3 执行失败
=== 场景 3:手动取消任务 ===
子任务 6 结果:子任务 6 执行完成
子任务 5 被取消
2.4 注意事项
  • 启用方式:需在编译和运行时添加 --enable-preview --release 21 参数(JDK 22/23 同理,JDK 23 该特性已正式发布,无需预览参数);
  • scope 生命周期:必须使用 try-with-resources 语法,确保自动关闭(触发未完成子任务中断);手动关闭需调用 shutdown(),但仍建议配合 try-with-resources;
  • 核心 scope 实现:
    • ShutdownOnSuccess:任一子任务成功则立即关闭其他任务,优先返回首个成功结果;
    • ShutdownOnFailure:任一子任务失败则立即关闭其他任务,失败信息存入 failures();
    • StructuredTaskScope:基础实现,需手动控制关闭 / 取消,灵活性最高;
  • 线程模型:fork() 方法默认使用 ForkJoinPool.commonPool()(平台线程),若要使用虚拟线程,需显式指定线程工厂:scope.fork(() -> {...}, Thread.ofVirtual().factory());
  • 异常处理:join() 可能抛出 InterruptedException(线程中断)、ExecutionException(子任务异常封装);所有子任务的原始异常需通过 scope.failures() 获取,而非直接捕获 join() 的异常;
  • 取消机制:shutdown() 仅标记 scope 为关闭状态,子任务需通过捕获 InterruptedException 响应取消。

3. 向量 API(Vector API,JEP 448)—— 提升数值计算性能

3.1 特性说明

向量 API 是 JDK 21 的孵化特性(Incubator)(经 JDK16-JDK20 多轮孵化优化),旨在提供一套高效的数值计算 API,充分利用 CPU 的向量指令(如 AVX2、AVX512、NEON),实现单指令多数据(SIMD)并行计算,大幅提升数组、矩阵等数值操作的性能(尤其适合科学计算、机器学习、信号处理等场景)。它提供类型安全的向量操作,避免手动编写汇编代码,同时支持跨平台,无需关注底层 CPU 架构差异。

核心优势:

  • 高性能:直接映射 CPU 向量指令,SIMD 并行计算,比传统循环快数倍(大规模计算场景);
  • 类型安全:强类型向量接口,编译时检查操作合法性,避免类型错误;
  • 跨平台:自动适配 x86、ARM 等不同 CPU 架构,无需修改代码;
  • 易用性:API 设计简洁,支持向量加减乘除、逻辑运算、聚合等常用操作;
  • 自适应:支持 SPECIES_PREFERRED 自动适配当前 CPU 最优向量长度。
3.2 代码样例(向量计算 + 性能对比)
import jdk.incubator.vector.FloatVector;
import jdk.incubator.vector.VectorSpecies;
import java.util.Arrays;

/**
 * 向量 API(孵化特性)演示 + 性能对比
 * 编译命令:javac --release 21 --add-modules jdk.incubator.vector VectorAPIDemo.java
 * 运行命令:java --add-modules jdk.incubator.vector VectorAPIDemo
 */
public class VectorAPIDemo {
    // 定义向量物种:自动适配当前 CPU 最优长度(推荐),float 类型
    private static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
    // 也可手动指定:FloatVector.SPECIES_128(128 位,4 个 float)、FloatVector.SPECIES_256(256 位,8 个 float)

    // 传统循环计算:两个 float 数组相加
    public static void arrayAddTraditional(float[] a, float[] b, float[] result) {
        for (int i = 0; i < a.length; i++) {
            result[i] = a[i] + b[i];
        }
    }

    // 向量 API 计算:两个 float 数组相加
    public static void arrayAddVector(float[] a, float[] b, float[] result) {
        int i = 0;
        // 向量批量处理(每次处理 SPECIES.length 个元素)
        int upperBound = SPECIES.loopBound(a.length);
        for (; i < upperBound; i += SPECIES.length) {
            // 从数组中加载向量
            FloatVector va = FloatVector.fromArray(SPECIES, a, i);
            FloatVector vb = FloatVector.fromArray(SPECIES, b, i);
            // 向量相加,结果存储到数组
            va.add(vb).intoArray(result, i);
        }
        // 处理剩余不足一个向量长度的元素
        for (; i < a.length; i++) {
            result[i] = a[i] + b[i];
        }
    }

    public static void main(String[] args) {
        // 初始化两个大数组(1000 万元素)
        int size = 10_000_000;
        float[] a = new float[size];
        float[] b = new float[size];
        float[] resultTraditional = new float[size];
        float[] resultVector = new float[size];

        // 填充数据(固定值,避免随机数影响性能)
        Arrays.fill(a, 1.23f);
        Arrays.fill(b, 4.56f);

        // JIT 预热:先运行一次,避免编译开销影响性能测试
        arrayAddTraditional(a, b, resultTraditional);
        arrayAddVector(a, b, resultVector);

        // 测试传统循环性能(多次运行取平均)
        long start = System.currentTimeMillis();
        for (int j = 0; j < 5; j++) {
            arrayAddTraditional(a, b, resultTraditional);
        }
        long traditionalTime = (System.currentTimeMillis() - start) / 5;
        System.out.println("传统循环平均耗时:" + traditionalTime + "ms");

        // 测试向量 API 性能(多次运行取平均)
        start = System.currentTimeMillis();
        for (int j = 0; j < 5; j++) {
            arrayAddVector(a, b, resultVector);
        }
        long vectorTime = (System.currentTimeMillis() - start) / 5;
        System.out.println("向量 API 平均耗时:" + vectorTime + "ms");

        // 验证结果一致性
        boolean isEqual = Arrays.equals(resultTraditional, resultVector);
        System.out.println("结果是否一致:" + isEqual);

        // 打印向量物种信息(当前 CPU 适配的长度)
        System.out.println("当前 CPU 最优向量长度(float 元素数):" + SPECIES.length);
    }
}
3.3 运行结果
传统循环平均耗时:30ms
向量 API 平均耗时:7ms
结果是否一致:true
当前 CPU 最优向量长度(float 元素数):8
3.4 注意事项
  • 启用方式(核心修正):
    • 编译:javac --release 21 --add-modules jdk.incubator.vector VectorAPIDemo.java;
    • 运行:java --add-modules jdk.incubator.vector VectorAPIDemo;
    • 无需 --enable-preview(孵化特性≠预览特性),需导入 jdk.incubator.vector 包。
  • 向量物种选择:
    • 优先使用 SPECIES_PREFERRED(自动适配当前 CPU 最优向量长度);
    • 手动选择需匹配 CPU 架构(x86 支持 SPECIES_128/256/512,ARM 支持 SPECIES_128/256)。
  • 适用场景:
    • 适合大规模数值计算(数组 / 矩阵运算,元素数 ≥ 10 万);
    • 小规模计算(< 1 万元素)因向量初始化开销,性能可能不如传统循环。
  • 数据类型支持:仅支持 float、double、int、long 等基本数值类型,不支持引用类型。
  • 性能优化:
    • 数组长度尽量为向量长度的整数倍,减少剩余元素循环;
    • 性能测试需做 JIT 预热(先运行几次),避免编译开销导致数据失真;
    • 确保 CPU 开启向量指令集(如 x86 的 AVX2/AVX512),否则性能无提升。

三、总结

JDK 21 作为 LTS 版本,核心正式特性聚焦于提升开发效率、程序性能和代码安全性,虚拟线程、模式匹配 for switch、记录模式、密封类、分代 ZGC、序列化集合六大正式特性,可直接应用于生产环境,尤其适合高并发、大数据、企业级应用场景;五大预览特性则为后续开发提供了更多可能性,可根据项目需求选择性试用(需注意预览特性可能在未来版本中调整)。

本文所有代码均经过 JDK 21 环境测试,可直接复制运行,建议开发者结合实际项目场景,合理运用这些新特性,提升开发效率和程序性能。后续可持续关注 JDK 版本更新,及时掌握新特性的优化和迭代。

目录

  1. 一、核心正式特性
  2. 1. 虚拟线程(Virtual Threads,JEP 444)—— 轻量级高并发利器
  3. 1.1 特性说明
  4. 1.2 代码样例(推荐第三种方式)
  5. 1.3 运行结果(关键信息)
  6. 1.4 注意事项
  7. 2. 模式匹配 for switch(Pattern Matching for switch,JEP 441)—— 简化分支判断
  8. 2.1 特性说明
  9. 2.2 代码样例(5 种常见场景)
  10. 2.3 运行结果
  11. 2.4 注意事项
  12. 3. 记录模式(Record Patterns,JEP 440)—— 简化数据解构
  13. 3.1 特性说明
  14. 3.2 代码样例(基础解构 + 嵌套解构 + 部分解构)
  15. 3.3 运行结果
  16. 3.4 注意事项
  17. 4. 密封类(Sealed Classes,JEP 409)—— 控制类继承
  18. 4.1 特性说明
  19. 4.2 代码样例(密封类 + 密封接口 + 多层密封)
  20. 4.3 运行结果
  21. 4.4 注意事项
  22. 5. 分代 ZGC(Generational ZGC,JEP 439)—— 提升 GC 性能
  23. 5.1 特性说明
  24. 5.2 启用方式(JVM 参数)
  25. 启用分代 ZGC(JDK21 正式特性,无需解锁实验性选项)
  26. 可选参数(优化大堆场景)
  27. 5.3 实际应用示例
  28. 运行 Spring Boot 应用并启用分代 ZGC(JDK21+)
  29. 进阶配置(指定年轻代大小 + 停顿时间目标)
  30. 5.4 注意事项
  31. 6. 序列化集合(Sequenced Collections,JEP 431)—— 有序集合标准化
  32. 6.1 特性说明
  33. 6.2 代码样例(三大接口完整演示)
  34. 6.3 运行结果
  35. 6.4 注意事项
  36. 二、预览特性(需启用 --enable-preview 参数)
  37. 1. 字符串模板(String Templates,JEP 430)—— 简化字符串拼接
  38. 1.1 特性说明
  39. 1.2 代码样例(完整用法演示)
  40. 1.3 运行结果
  41. 1.4 注意事项
  42. 2. 结构化并发(Structured Concurrency,JEP 453)—— 简化并发任务管理
  43. 2.1 特性说明
  44. 2.2 代码样例(基础使用 + 异常处理 + 取消任务)
  45. 2.3 运行结果
  46. 2.4 注意事项
  47. 3. 向量 API(Vector API,JEP 448)—— 提升数值计算性能
  48. 3.1 特性说明
  49. 3.2 代码样例(向量计算 + 性能对比)
  50. 3.3 运行结果
  51. 3.4 注意事项
  52. 三、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 量化实战:AKshare 零成本获取全市场金融数据
  • uView Pro:基于 Vue3 的 uni-app 跨端 UI 组件库实践
  • Git 版本控制核心命令与实战流程
  • Claude Code 核心执行模式与辅助功能解析
  • 法奥机器人控制器基础操作与 Lua 编程实战
  • Nano Banana 生成中文模糊?用 Seedream 4.5 重绘文字提升清晰度
  • C++ STL 容器入门:set 与 map 详解
  • C 语言实现顺时针旋转矩阵
  • MCP 协议层详解:深入分析 Python SDK 中的实现机制
  • C 语言入门指南:核心特性与快速上手
  • 昇腾平台 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践
  • Linux 源配置与包管理:CentOS/Ubuntu 更新及 Vim 基础
  • OSCP 实战:Net-NTLMv2 哈希传递与中继攻击详解
  • Linux 部署 OpenClaw 并接入 QQ 机器人指南
  • Python 进阶:多重继承 MRO 与 C3 线性化算法解析
  • 算法入门:双指针算法(一)
  • 自然语言处理在教育领域的应用与实战
  • 普通人零基础进入 AIGC 大模型人形机器人赛道自学攻略与应用案例
  • Streamlit 实战:快速构建数据驱动 Web 应用
  • 提升 SQL 技能的 7 个最佳练习平台

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online