跳转至

Java21 新特性

JEP 430 String Templates(字符串模版)

旨在彻底改变 Java 中拼接和格式化字符串的方式,使其更安全、简洁和直观。

这个特性的核心是引入了模板表达式 (Template Expressions),允许在字符串中直接嵌入变量和表达式。

核心优势

  • 安全性 (Security): 能够防止常见的注入攻击(如 SQL 注入、HTML 注入),因为模板处理器可以在拼接前对输入进行验证和转义。
  • 可读性 (Readability): 告别繁琐的 + 号拼接、StringBuilder 或难读的 String.format 占位符。
  • 灵活性 (Flexibility): 不仅支持字符串插值,还可以通过自定义处理器将模板转换为任何类型的对象(如 JSON 对象、数据库查询对象等)。

语法详解

字符串模板的语法由三部分组成: 1. 模板处理器 (Template Processor): 如 STR、FMT 或 RAW。 2. 点号 (.): 连接处理器和模板内容。 3. 包含嵌入表达式的模板: 变量或表达式被包裹在 {} 中。

1. STR 处理器 (标准插值)

STR 是最常用的处理器,用于简单的字符串插值。它会自动将 {} 中的变量替换为字符串值。

String name = "World";
String message = STR."Hello, \{name}!"; 
// 结果: "Hello, World!"

int x = 10, y = 20;
String result = STR."\{x} + \{y} = \{x + y}"; 
// 结果: "10 + 20 = 30"

2. FMT 处理器 (格式化插值)

FMT 类似于 String.format,除了插值外,还支持在变量前添加格式说明符(如 %d, %.2f)。

double price = 12.3456;
String json = FMT."The price is %.2f\{price}"; 
// 结果: "The price is 12.35"

3. RAW 处理器 (原始模板)

RAW 不会立即进行拼接,而是返回一个 StringTemplate 对象。这允许你延迟处理,或者通过自定义处理器来处理模板内容 。

StringTemplate st = RAW."User: \{name}";
// 此时并未生成最终字符串,可以稍后处理

多行模板 (Text Blocks)

字符串模板完美支持 Java 的文本块(Text Blocks),非常适合生成 JSON、HTML 或 SQL。

String title = "My Page";
String content = "Hello";
String html = STR."""
    <html>
        <head><title>\{title}</title></head>
        <body><p>\{content}</p></body>
    </html>
    """;

注意事项

由于这是一个预览特性,在 Java 21 中使用时需要添加 --enable-preview 参数来编译和运行代码。

javac --enable-preview --release 21 Main.java
java --enable-preview Main

JEP 440 记录模式(Record Patterns)

它是 Java 语言向“数据导向编程”转型的关键一步。

简单来说,记录模式允许你像“拆包裹”一样,直接把 Record 对象的内部字段解构(Deconstruct)出来,而不需要手动调用 getter 方法。

核心功能:解构(Deconstruction)

在 Java 21 之前,即使使用 Java 16 的 instanceof 模式匹配,你仍然需要通过变量来访问字段:

record Point(int x, int y) {}

static void printSum(Object obj) {
    // Java 16 写法:先匹配类型,再通过 p.x() 访问
    if (obj instanceof Point p) {
        int x = p.x();
        int y = p.y();
        System.out.println(x + y);
    }
}

Java 21 的记录模式允许你直接在匹配时定义变量来接收字段值:

static void printSum(Object obj) {
    // Java 21 写法:直接拆解出 x 和 y
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x + y); 
    }
}
  • Point(int x, int y) 就是一个记录模式。
  • 如果 obj 是 Point 类型,它会被自动转换,且其内部的 x 和 y 组件会被直接赋值给局部变量 x 和 y 。

进阶用法

  1. 嵌套解构 (Nested Patterns) 这是记录模式最强大的地方。如果一个 Record 内部包含另一个 Record,你可以一次性把最里面的数据拆出来。
record Point(int x, int y) {}
record Rect(Point topLeft, Point bottomRight) {}

static void printRectInfo(Object obj) {
    // 直接拆解出嵌套的坐标值,无需 obj.topLeft().x()
    if (obj instanceof Rect(Point(int x1, int y1), Point(int x2, int y2))) {
        System.out.println("Rect width: " + (x2 - x1));
    }
}

这种写法消除了层层调用的代码(如 r.topLeft().x()),且自动处理了空指针检查(如果 obj 为 null,匹配直接失败)。 ​

  1. 类型推断 (Type Inference) 你可以使用 var 来进一步简化代码,让编译器自动推断字段类型。
if (obj instanceof Point(var x, var y)) {
    // x 和 y 的类型自动推断为 int
    System.out.println(x + y);
}
  1. Switch 模式匹配 (Switch Expressions) 记录模式与 Switch 表达式结合,可以处理复杂的类型判断逻辑。
record Circle(double radius) {}
record Rect(double w, double h) {}

void printShape(Object shape) {
    switch (shape) {
        case Circle(var r)     -> System.out.println("Circle radius: " + r);
        case Rect(var w, var h) -> System.out.println("Rect area: " + w * h);
        case null              -> System.out.println("Null shape");
        default                -> System.out.println("Unknown shape");
    }
}

这种写法比传统的 if-else 链更加清晰且强制完整性检查(Exhaustiveness Checking)。

总结

记录模式不仅仅是语法糖,它改变了我们处理数据的方式:从“如何获取数据”(调用 getter)转变为“声明我想要的数据结构”(模式匹配)。 配合 Switch 表达式,它使得处理复杂数据结构(如 JSON 树、AST 语法树)变得异常简洁。


JEP 439 分代ZGC(Generational ZGC)

分代 ZGC (Generational ZGC) 是 Java 21 中的一项重大性能升级(JEP 439),标志着 ZGC 从“不分代”正式进化为“分代”模式。

简单来说,它让 ZGC 变“聪明”了:它不再对所有对象一视同仁地进行昂贵的全堆扫描,而是专注于频繁回收那些“短命”的年轻对象。

核心原理:为什么需要“分代”? 在传统的 ZGC(Java 21 之前)中,垃圾回收是“单代”的(Single Generation),无论对象是刚创建的还是已经存活很久的,ZGC 都会一视同仁地混合在一起管理。

弱分代假说 (Weak Generational Hypothesis): 实际上,绝大多数 Java 对象都是“朝生夕死”的(如 HTTP 请求中的临时变量)。

分代 ZGC 的改进: 它将堆内存逻辑上划分为年轻代 (Young Generation) 和 老年代 (Old Generation)。

年轻代: 存放新创建的对象。ZGC 会非常频繁地扫描这里,因为这里垃圾最多,回收性价比最高。

老年代: 存放经过多次 GC 仍存活的对象。ZGC 很少打扰这里,从而节省大量 CPU 资源 。

性能提升数据 相比于不分代的 ZGC,分代 ZGC 带来了显著的提升:

吞吐量 (Throughput): 提升超过 50%。因为 CPU 不再浪费时间去重复扫描那些长期存活的老对象 。 ​

内存开销 (Memory Overhead): 减少约 75%。原本需要大量额外的堆内存来维持不分代的并发处理,现在这部分需求大幅降低。

回收开销 (Allocation Stall): 大幅降低分配停顿的风险。

如何使用? 在 Java 21 中,分代 ZGC 已经成为标准特性,但默认可能尚未开启(取决于具体发行版配置,通常建议显式开启)。

bash

开启分代 ZGC

java -XX:+UseZGC -XX:+ZGenerational -jar your-app.jar (注意:在后续版本中,分代模式将成为 ZGC 的默认行为,届时 -XX:+ZGenerational 可能不再需要) 。

总结 分代 ZGC 解决了传统 ZGC 在高并发、高分配速率下的“软肋”。它保留了 ZGC 标志性的亚毫秒级停顿 (Sub-millisecond pauses),同时补齐了吞吐量短板,使其真正成为能够应对从几百 MB 到数 TB 级内存的通用型垃圾收集器 。