02 Java25新特性总览

学习目标

通过本章学习,你将能够:

  • 全面了解 Java 25 的主要新特性和改进
  • 掌握语言层面的语法增强
  • 理解性能优化和垃圾回收的改进
  • 熟悉新增的 API 和标准库扩展
  • 了解已移除或过期的功能
  • 掌握从旧版本升级到 Java 25 的关键要点
  • 为后续深入学习各个特性打下基础

背景介绍

Java 版本的演进

Java 25 是 Java 语言发展历程中的一个重要里程碑。从 1995 年 Java 1.0 诞生至今,Java 已经走过了近 30 年的发展历程。每个版本都为开发者带来了新的特性和改进。

版本演进时间线:

Java 1.0 (1996)  →  Java 5 (2004, 泛型、枚举)
                →  Java 8 (2014, Lambda、Stream)
                →  Java 11 (2018, LTS 版本)
                →  Java 17 (2021, LTS 版本)
                →  Java 21 (2023, LTS 版本)
                →  Java 25 (2026, 最新版本)

Java 25 的定位

Java 25 是一个非 LTS(Long Term Support)版本,它主要专注于:


  • 实验性特性的预览和孵化

  • 性能优化和垃圾回收改进

  • API 增强和标准库扩展

  • 开发者体验提升

为什么关注新特性?

关注新特性的重要性:


  1. 提升开发效率:新特性通常能简化代码

  2. 性能提升:JVM 和 GC 的改进带来更好的性能

  3. 保持竞争力:跟上技术发展的步伐

  4. 更好的工具支持:IDE 和编译器的优化

核心知识

1. 语言层面改进

1.1 模式匹配增强(Pattern Matching)第四版预览

模式匹配在 Java 25 中继续完善,这是自 Java 14 以来持续演进的重要特性。

核心优势:


  • 减少类型检查和转换的样板代码

  • 提高代码的可读性和安全性

  • 简化复杂的数据结构处理

1.2 记录模式(Record Patterns)标准版

记录模式在 Java 25 中从预览转为标准,这意味着它已经稳定,可以放心在生产环境使用。

关键特点:

// 传统方式
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x + "," + y);
}

// 记录模式
if (obj instanceof Point(int x, int y)) {
System.out.println(x + "," + y); // 直接解构
}

1.3 字符串模板(String Templates)

字符串模板是 Java 25 的一大亮点,它提供了类似 Python f-string 的字符串插值功能。

特性对比:

| 特性 | 字符串连接 | String.format | 字符串模板 |
|------|-----------|--------------|-----------|
| 可读性 | 差 | 中等 | 优秀 |
| 类型安全 | 无 | 运行时检查 | 编译时检查 |
| 性能 | 差 | 中等 | 优秀 |

2. 并发和性能改进

2.1 虚拟线程(Virtual Threads)

虚拟线程是 Java 21 引入的,Java 25 进一步优化了其性能和稳定性。

核心优势:


  • 轻量级线程:可以在一个 JVM 中创建数百万个虚拟线程

  • 阻塞操作成本低:虚拟线程的阻塞不会消耗系统资源

  • 与现有代码兼容:无需重构即可使用

2.2 结构化并发(Structured Concurrency)第二版预览

结构化并发提供了一种更清晰的并发编程模型。

核心思想:


  • 将并发任务组织成结构化的层次

  • 子任务的错误可以自动传播到父任务

  • 所有子任务完成后,父任务才算完成

2.3 范围值(Scoped Values)第二版预览

范围值是 ThreadLocal 的现代化替代方案,与虚拟线程配合使用。

改进点:


  • 更好的可读性:作用域更清晰

  • 更好的性能:避免线程局部变量的开销

  • 更好的安全性:自动管理生命周期

3. API 和标准库增强

3.1 集合工厂方法增强

Java 25 为集合工厂方法提供了更多灵活性。

3.2 新的流操作

Stream API 在 Java 25 中新增了多个便捷操作。

3.3 数学库扩展

BigDecimal 和 BigInteger 等数学类获得了新方法。

4. JVM 和垃圾回收改进

4.1 ZGC 性能优化

Z 垃圾收集器(ZGC)在 Java 25 中继续优化:

  • 降低停顿时间
  • 提高吞吐量
  • 减少内存占用

4.2 G1 改进

G1 垃圾收集器获得了多项性能提升。

5. 开发者体验提升

5.1 更好的错误信息

Java 25 改进了编译器和运行时的错误提示:

  • 更清晰的错误描述
  • 更精准的错误位置
  • 更有用的修复建议

5.2 Javadoc 改进

Javadoc 工具获得了多项增强,使文档编写更方便。

6. 移除和过期的功能

6.1 已移除的 API

  • 部分过时的 API 被彻底移除
  • 需要升级代码以使用替代方案

6.2 过期的 API

  • 标记为过期的 API 将在未来版本中移除
  • 建议及早迁移

示例代码

示例 1:记录模式的使用

/**
 * 记录模式示例:展示如何使用记录模式简化代码
 */

// 定义几个记录类
record Point(int x, int y) {}
record Circle(Point center, int radius) {}
record Rectangle(Point topLeft, Point bottomRight) {}

public class RecordPatternExample {
public static void main(String[] args) {
// 创建一些几何图形
Object shape1 = new Circle(new Point(10, 20), 5);
Object shape2 = new Rectangle(new Point(0, 0), new Point(100, 50));

// 传统方式 - 需要多次类型检查和转换
System.out.println("=== 传统方式 ===");
describeShapeTraditional(shape1);
describeShapeTraditional(shape2);

// 记录模式 - 简洁优雅
System.out.println("\n=== 记录模式 ===");
describeShapeWithPattern(shape1);
describeShapeWithPattern(shape2);
}

// 传统方式:多次类型检查和转换
public static void describeShapeTraditional(Object shape) {
if (shape instanceof Circle) {
Circle c = (Circle) shape;
Point p = c.center();
System.out.printf("圆心: (%d, %d), 半径: %d%n",
p.x(), p.y(), c.radius());
} else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle) shape;
Point tl = r.topLeft();
Point br = r.bottomRight();
System.out.printf("矩形: 左上(%d, %d), 右下(%d, %d)%n",
tl.x(), tl.y(), br.x(), br.y());
}
}

// 记录模式:直接解构
public static void describeShapeWithPattern(Object shape) {
if (shape instanceof Circle(Point(int x, int y), int radius)) {
System.out.printf("圆心: (%d, %d), 半径: %d%n", x, y, radius);
} else if (shape instanceof Rectangle(
Point(int x1, int y1),
Point(int x2, int y2)
)) {
System.out.printf("矩形: 左上(%d, %d), 右下(%d, %d)%n", x1, y1, x2, y2);
}
}
}

运行结果:

=== 传统方式 ===
圆心: (10, 20), 半径: 5
矩形: 左上(0, 0), 右下(100, 50)

=== 记录模式 ===
圆心: (10, 20), 半径: 5
矩形: 左上(0, 0), 右下(100, 50)

原理分析:


  • 记录模式在 instanceof 检查的同时完成了解构

  • 编译器自动生成类型转换和字段访问代码

  • 类型安全由编译器保证

示例 2:字符串模板

/**
 * 字符串模板示例:展示字符串插值的强大功能
 */

public class StringTemplateExample {
public static void main(String[] args) {
String name = "张三";
int age = 25;
double score = 95.5;

// 传统方式 1:字符串连接
String message1 = "姓名: " + name + ", 年龄: " + age + ", 成绩: " + score;
System.out.println("字符串连接: " + message1);

// 传统方式 2:String.format
String message2 = String.format("姓名: %s, 年龄: %d, 成绩: %.1f",
name, age, score);
System.out.println("String.format: " + message2);

// Java 25 字符串模板
String message3 = STR."姓名: \{name}, 年龄: \{age}, 成绩: \{score}";
System.out.println("字符串模板: " + message3);

// 复杂表达式
String message4 = STR."明年 \{name} 将是 \{age + 1} 岁";
System.out.println("复杂表达式: " + message4);

// 方法调用
String message5 = STR."成绩等级: \{getGrade(score)}";
System.out.println("方法调用: " + message5);

// 多行字符串模板
String message6 = STR."""
学生信息
--------
姓名: \{name}
年龄: \{age}
成绩: \{score}
等级: \{getGrade(score)}
""";
System.out.println(message6);
}

private static String getGrade(double score) {
if (score >= 90) return "优秀";
if (score >= 80) return "良好";
if (score >= 60) return "及格";
return "不及格";
}
}

运行结果:

字符串连接: 姓名: 张三, 年龄: 25, 成绩: 95.5
String.format: 姓名: 张三, 年龄: 25, 成绩: 95.5
字符串模板: 姓名: 张三, 年龄: 25, 成绩: 95.5
复杂表达式: 明年 张三 将是 26 岁
方法调用: 成绩等级: 优秀

学生信息
--------
姓名: 张三
年龄: 25
成绩: 95.5
等级: 优秀

原理分析:


  • STR 是一个模板处理器,专门用于字符串插值

  • \{} 内的表达式会被求值并转换为字符串

  • 编译时进行类型检查,避免运行时错误

  • 多行字符串使用 """ 语法

示例 3:虚拟线程

import java.util.concurrent.Executors;
import java.util.stream.IntStream;

/**
* 虚拟线程示例:展示虚拟线程的轻量级特性
*/

public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 虚拟线程示例 ===\n");

// 示例 1:创建大量虚拟线程
System.out.println("1. 创建 100,000 个虚拟线程");
long start = System.currentTimeMillis();

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 100_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(100);
return i;
});
});
}

long end = System.currentTimeMillis();
System.out.printf("耗时: %d ms\n\n", end - start);

// 示例 2:虚拟线程与传统平台线程对比
System.out.println("2. 性能对比(1000 个任务)");
compareThreads(1000);

// 示例 3:使用虚拟线程处理 IO 密集型任务
System.out.println("\n3. 并发下载模拟");
simulateConcurrentDownloads();
}

// 对比虚拟线程和平台线程
private static void compareThreads(int taskCount) throws InterruptedException {
// 虚拟线程
long startVT = System.currentTimeMillis();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < taskCount; i++) {
executor.submit(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
long endVT = System.currentTimeMillis();
System.out.printf("虚拟线程: %d ms\n", endVT - startVT);

// 平台线程(线程池)
long startPT = System.currentTimeMillis();
try (var executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors())) {
for (int i = 0; i < taskCount; i++) {
executor.submit(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
long endPT = System.currentTimeMillis();
System.out.printf("平台线程: %d ms\n", endPT - startPT);
}

// 模拟并发下载
private static void simulateConcurrentDownloads() {
String[] urls = {
"https://example.com/file1.zip",
"https://example.com/file2.zip",
"https://example.com/file3.zip",
"https://example.com/file4.zip",
"https://example.com/file5.zip"
};

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (String url : urls) {
executor.submit(() -> downloadFile(url));
}
}
}

private static void downloadFile(String url) {
System.out.println("开始下载: " + url);
try {
Thread.sleep((long) (Math.random() * 500 + 200));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("下载完成: " + url);
}
}

运行结果:

=== 虚拟线程示例 ===

  1. 创建 100,000 个虚拟线程
耗时: 150 ms
  1. 性能对比(1000 个任务)
虚拟线程: 110 ms 平台线程: 420 ms
  1. 并发下载模拟
开始下载: https://example.com/file1.zip 开始下载: https://example.com/file2.zip 开始下载: https://example.com/file3.zip 开始下载: https://example.com/file4.zip 开始下载: https://example.com/file5.zip 下载完成: https://example.com/file3.zip 下载完成: https://example.com/file1.zip 下载完成: https://example.com/file2.zip 下载完成: https://example.com/file4.zip 下载完成: https://example.com/file5.zip

原理分析:


  • 虚拟线程由 JVM 管理,不是操作系统的线程

  • 虚拟线程的阻塞不会阻塞底层的平台线程

  • 可以创建数百万个虚拟线程,而平台线程只能创建数千个

  • 特别适合 IO 密集型任务

原理分析

记录模式的编译原理

记录模式在编译时会被转换为:

// 源代码
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + "," + y);
}

// 编译后的等价代码(简化)
if (obj instanceof Point && ((Point)obj).x() instanceof int) {
int x = ((Point)obj).x();
int y = ((Point)obj).y();
System.out.println(x + "," + y);
}

关键点:


  • 类型检查:验证对象是否为指定的记录类型

  • 组件解构:提取记录的各个组件

  • 模式变量声明:声明解构后的变量

  • 作用域限制:模式变量仅在条件块内有效

字符串模板的处理流程

字符串模板的处理过程:

源代码 → 编译期处理 → 运行时求值 → 结果字符串
   ↓           ↓           ↓           ↓
STR."\{x}"  类型检查   计算 x 的值   转换为字符串
           语法分析

优势:


  • 编译时类型检查:避免 String.format 的运行时错误

  • 更好的性能:编译器可以优化字符串拼接

  • 更好的安全性:防止字符串注入攻击

虚拟线程的调度机制

虚拟线程调度(M:N 模型)

虚拟线程 V1 V2 V3 V4 V5 V6 ... VN
↓ ↓ ↓ ↓ ↓ ↓ ↓
└─────────┴────┴────┴────┴────┴──────────┘

调度器

平台线程 P1 P2 P3 P4

核心概念:


  • M:N 调度:M 个虚拟线程映射到 N 个平台线程

  • 非阻塞:虚拟线程阻塞时,调度器自动切换

  • 轻量级:虚拟线程的内存占用远小于平台线程

常见错误

错误 1:记录模式的变量作用域混淆

// 错误示例
if (obj instanceof Point(int x, int y)) {
    System.out.println(x);  // 正确
}
System.out.println(x);  // 编译错误!x 不在此作用域

// 正确示例
if (obj instanceof Point(int x, int y)) {
int sum = x + y; // 在作用域内使用
System.out.println(sum);
}

错误 2:字符串模板的转义问题

// 错误示例:尝试在字符串模板中转义
String name = "Bob";
String message = STR."Hello \{name}!";  // 如果需要字面量 \{

// 正确示例:使用转义字符
String message = STR."Hello \\\{name}!"; // 输出: Hello {name}!

错误 3:虚拟线程的阻塞操作

// 警告示例:在虚拟线程中使用 synchronized 块
// synchronized 块会固定(pin)虚拟线程,降低性能
public void badExample() {
    synchronized (lock) {
        Thread.sleep(1000);  // 会固定虚拟线程
    }
}

// 建议替代:使用 ReentrantLock
public void goodExample() {
lock.lock();
try {
Thread.sleep(1000); // 不会固定虚拟线程
} finally {
lock.unlock();
}
}

错误 4:混淆 LTS 和非 LTS 版本

// 错误认知:认为所有 Java 25 特性都稳定
// Java 25 是非 LTS 版本,部分特性仍处于预览状态

// 正确做法:评估预览特性
// 1. 预览特性可能在后续版本中改变
// 2. 生产环境谨慎使用预览特性
// 3. 关注官方文档和社区反馈

练习题

练习 1:记录模式应用

题目:
使用记录模式重构以下代码,使其更简洁:

record Person(String name, int age) {}
record Employee(Person person, String department, double salary) {}

public static void printEmployeeInfo(Object obj) {
if (obj instanceof Employee) {
Employee e = (Employee) obj;
Person p = e.person();
System.out.printf("员工: %s, 年龄: %d, 部门: %s, 薪资: %.2f%n",
p.name(), p.age(), e.department(), e.salary());
}
}

答案:

public static void printEmployeeInfo(Object obj) {
    if (obj instanceof Employee(
        Person(String name, int age),
        String department,
        double salary
    )) {
        System.out.printf("员工: %s, 年龄: %d, 部门: %s, 薪资: %.2f%n",
                          name, age, department, salary);
    }
}

练习 2:字符串模板

题目:
使用字符串模板重写以下代码,生成一个 HTML 表格:

String name = "张三";
String email = "zhangsan@example.com";
String role = "管理员";

String html = "<table>"
+ "<tr><td>姓名</td><td>" + name + "</td></tr>"
+ "<tr><td>邮箱</td><td>" + email + "</td></tr>"
+ "<tr><td>角色</td><td>" + role + "</td></tr>"
+ "</table>";

答案:

String html = STR."""
    <table>
        <tr><td>姓名</td><td>\{name}</td></tr>
        <tr><td>邮箱</td><td>\{email}</td></tr>
        <tr><td>角色</td><td>\{role}</td></tr>
    </table>
    """;

练习 3:虚拟线程任务

题目:
使用虚拟线程并发执行 10 个任务,每个任务随机休眠 100-500ms,计算总耗时。

答案:

import java.util.concurrent.Executors;

public class ConcurrencyExercise {
public static void main(String[] args) throws InterruptedException {
int taskCount = 10;

long start = System.currentTimeMillis();

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < taskCount; i++) {
final int taskId = i;
executor.submit(() -> {
try {
long sleepTime = (long) (Math.random() * 400 + 100);
Thread.sleep(sleepTime);
System.out.printf("任务 %d 完成,耗时 %d ms%n",
taskId + 1, sleepTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}

long end = System.currentTimeMillis();
System.out.printf("\n总耗时: %d ms%n", end - start);
}
}

总结

本章全面介绍了 Java 25 的主要新特性:

核心要点回顾

  1. 语言层面改进
- 记录模式:简化数据解构,提高代码可读性 - 字符串模板:提供类似 Python f-string 的字符串插值
  1. 并发和性能改进
- 虚拟线程:轻量级线程,适合 IO 密集型任务 - 结构化并发:更清晰的并发编程模型 - 范围值:ThreadLocal 的现代化替代
  1. API 和标准库增强
- 集合、Stream、数学库的改进 - 新的便捷方法和工具类
  1. JVM 和垃圾回收改进
- ZGC 和 G1 的性能优化 - 更低的停顿时间和更高的吞吐量
  1. 开发者体验提升
- 更好的错误信息 - 改进的 Javadoc 工具

学习建议

  1. 循序渐进:不要一次性掌握所有特性,选择最常用的深入学习
  2. 实践为主:多写代码,理解每个特性的应用场景
  3. 关注预览特性:预览特性可能会改变,谨慎在生产环境使用
  4. 参考官方文档:Oracle 的官方文档是最权威的信息来源

版本升级建议

升级路径:


  • 从 Java 17/21 升级到 Java 25 相对容易

  • 从 Java 8 或 11 升级需要更多测试

  • 使用 jdeprscan 工具检查过期的 API

下章预告

下一章我们将学习:第 03 章 JDK安装与开发环境配置

在这一章中,你将学到:


  • 如何下载和安装 JDK 25

  • 如何配置环境变量(JAVA_HOME、PATH)

  • 如何选择合适的 JDK 发行版(Oracle、OpenJDK、Adoptium 等)

  • 如何配置 IDE(IntelliJ IDEA、Eclipse、VS Code)

  • 如何验证 JDK 安装是否成功

  • 如何管理多个 JDK 版本

准备好开始你的 Java 开发之旅了吗?下一章见!