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 。
进阶用法¶
- 嵌套解构 (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,匹配直接失败)。
- 类型推断 (Type Inference) 你可以使用 var 来进一步简化代码,让编译器自动推断字段类型。
if (obj instanceof Point(var x, var y)) {
// x 和 y 的类型自动推断为 int
System.out.println(x + y);
}
- 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 级内存的通用型垃圾收集器 。