专业的JAVA编程教程与资源

网站首页 > java教程 正文

深度解析 Java 21 新特性,助力互联网软件开发升级

temp10 2025-08-02 21:41:39 java教程 4 ℃ 0 评论

在互联网软件开发领域,Java 一直占据着举足轻重的地位。随着技术的飞速发展,Java 也在不断进化,为开发者们带来更多高效、便捷的工具和特性。Java 21 于 2023 年 9 月 19 日正式发布,作为继 JDK 17 之后的长期支持(LTS)版本,它带来了诸多令人振奋的新特性,为广大互联网软件开发人员提供了更强大的编程能力。接下来,就让我们深入探究 Java 21 的新特性,看看它们将如何影响我们的开发工作。

字符串模板(String Templates)—— 预览特性

特性概述

深度解析 Java 21 新特性,助力互联网软件开发升级

在大多数编程语言中,字符串插值已广泛应用,而 Java 21 通过字符串模板这一预览特性,也加入了这一行列。字符串模板提供了一种更简洁、直观的方式来动态构建字符串。以往在 Java 中,我们通常使用字符串拼接或格式化方法来构建字符串,例如使用+运算符进行字符串连接,代码可能会变得难以阅读;使用StringBuilder进行复杂字符串组合时,代码较为冗长;String::format和String::formatted将格式字符串从参数中分离,不够直观;java.text.MessageFormat需要在格式字符串中使用不方便阅读的数字索引。而现在,通过字符串模板,我们可以使用占位符<代码开始>${}<代码结束>,将变量的值直接嵌入到字符串中,在运行时,Java 编译器会将这些占位符替换为实际的变量值,并且表达式支持局部变量、静态 / 非静态字段甚至方法、计算结果等特性。

模板处理器

Java 目前支持三种模板处理器:

STR:自动执行字符串插值,即将模板中的每个嵌入式表达式替换为其值(转换为字符串)。例如:

String name = "张三";
String greeting = STR."Hello, {name}!";
System.out.println(greeting); 

运行结果将输出Hello, 张三! 。

FMT:和 STR 类似,但它还可以接受格式说明符,这些格式说明符出现在嵌入式表达式的左边,用来控制输出的样式。比如:

int number = 123;
String formatted = FMT."%05d{number}";
System.out.println(formatted); 

这里输出的结果会是00123 ,通过%05d格式说明符将数字格式化为 5 位,不足的用 0 填充。

RAW:不会像 STR 和 FMT 模板处理器那样自动处理字符串模板,而是返回一个StringTemplate对象,这个对象包含了模板中的文本和表达式的信息。在需要对模板进行更底层操作时会用到。

除了 JDK 自带的三种模板处理器外,开发者还可以实现StringTemplate.Processor接口来创建自己的模板处理器,只需要继承该接口,然后实现process方法即可,这大大增强了字符串模板的灵活性。

序列化集合(Sequenced Collections)

特性介绍

JDK 21 引入了一种新的集合类型 ——Sequenced Collections(序列化集合,也叫有序集合),这是一种具有确定出现顺序(encounter order)的集合。无论遍历这样的集合多少次,元素的出现顺序始终是固定的。序列化集合提供了处理集合的第一个和最后一个元素以及反向视图(与原始集合相反的顺序)的简单方法。

相关接口

Sequenced Collections 包括以下三个接口:

SequencedCollection接口继承了Collection接口,提供了在集合两端访问、添加或删除元素以及获取集合的反向视图的方法。以ArrayList为例,我们可以这样使用:

SequencedCollection<String> sequencedList = new ArrayList<>();
sequencedList.addFirst("First Element");
sequencedList.addLast("Last Element");
System.out.println(sequencedList.getFirst()); 
System.out.println(sequencedList.getLast()); 
SequencedCollection<String> reversedList = sequencedList.reversed();
for (String element : reversedList) {
    System.out.println(element); 
}

SequencedSet接口直接继承了SequencedCollection接口并重写了reversed()方法。SortedSet和LinkedHashSet实现了SequencedSet接口。

SequencedMap接口继承了Map接口,提供了在集合两端访问、添加或删除键值对、获取包含 key 的SequencedSet、包含 value 的SequencedCollection、包含 entry(键值对)的SequencedSet以及获取集合的反向视图的方法。SortedMap和LinkedHashMap实现了SequencedMap接口。

通过这些接口,我们在处理有序集合时将更加方便,无论是获取首尾元素,还是反向遍历集合,都有了统一且简洁的操作方式。

分代 ZGC(Generational ZGC)

功能扩展

JDK21 中对 ZGC 进行了功能扩展,增加了分代 GC 功能。在 Java 的垃圾回收机制中,对象的生命周期各不相同,年轻对象往往很快就不再被使用,而年老对象则存活时间较长。分代 ZGC 通过扩展 Z 垃圾回收器(ZGC),为年轻对象和年老对象分别保留不同的世代,这样 ZGC 就能够更频繁地收集年轻代的对象,因为这些年轻对象往往 “英年早逝”,从而提高应用程序的性能。

启用方式

不过,分代 ZGC 默认是关闭的,需要通过配置打开。在未来的版本中,官方会把 ZGenerational 设为默认值,即默认打开 ZGC 的分代 GC。在更晚的版本中,非分代 ZGC 可能会被移除。例如在启动 JVM 时,可以通过添加参数-XX:+ZGenerational来开启分代 ZGC 功能。

记录模式(Record Patterns)

功能增强

Java 21 使用记录模式来解构记录值,从而增强了 Java 编程语言的功能。记录模式和类型模式可以嵌套,以实现强大、声明性和可组合的数据导航和处理形式。在 Java 16 中引入了 record 类,它是一种特殊的类,用于以一种紧凑的方式表示不可变的数据载体。当我们想要获取、操作或者打印 record 类中的数据时,在 Java 21 之前,往往不得不先完成类型判断和类型转换,代码较为繁琐。而有了记录模式后,代码得到了极大的简化。

代码示例

比如定义一个名为 Point 的 record 类:

public record Point(int x, int y) {}

在 Java 21 之前,如果要实现获取Point中的x和y并进行计算,可能需要这样写:

Object obj = new Point(3, 4);
if (obj instanceof Point point) {
    int x = point.x();
    int y = point.y();
    int result = x + y;
    System.out.println(result); 
}

而在 Java 21 之后,只需要:

Object obj = new Point(3, 4);
if (obj instanceof Point(int x, int y)) {
    int result = x + y;
    System.out.println(result); 
}

可以看到,类型判断、类型转换、record 值的解构都一气呵成,代码量大大减少,且更加简洁明了。

switch 的模式匹配(Pattern Matching for switch)

特性进化

在 Java 5 中,switch首次引入,仅支持整型数据类型(如byte,short,char和int)及枚举类型。到了 Java 12,switch表达式作为预览特性引入,支持了箭头函数。Java 13 中对switch表达式做了增强改进,在块中引入了yield语句来返回值,而不是使用break。Java 16 中,JEP 394将instanceof的模式匹配发布为正式属性。而在 Java 17 中,引入了模式匹配的switch表达式特性,但当时为预览特性。在 Java 21 中,switch的模式匹配终于成为一个正式特性。

代码对比

该特性扩展了switch表达式和语句,允许它们使用模式匹配,这意味着我们可以在switch的case标签中使用模式,如类型模式,使得代码更加灵活和表达性更强,并且无需进行显式的类型转换。例如,假设有一个诗人类(Poet),它有 3 个实现类:唐朝诗人(TangPoet)、宋朝诗人(SongPoet)、汉朝诗人(HanPoet),要根据诗人的类型进行不同处理,在 Java 17 之前,可能需要这样写:

Poet poet = new TangPoet();
if (poet instanceof TangPoet) {
    TangPoet tangPoet = (TangPoet) poet;
    // 处理唐朝诗人的逻辑
} else if (poet instanceof SongPoet) {
    SongPoet songPoet = (SongPoet) poet;
    // 处理宋朝诗人的逻辑
} else if (poet instanceof HanPoet) {
    HanPoet hanPoet = (HanPoet) poet;
    // 处理汉朝诗人的逻辑
}

而在 Java 21 中,使用switch的模式匹配可以这样写:

Poet poet = new TangPoet();
switch (poet) {
    case TangPoet tangPoet -> {
        // 处理唐朝诗人的逻辑
    }
    case SongPoet songPoet -> {
        // 处理宋朝诗人的逻辑
    }
    case HanPoet hanPoet -> {
        // 处理汉朝诗人的逻辑
    }
}

代码变得简洁明了,并且类型转换的工作由switch的模式匹配自动完成,减少了出错的可能性。

虚拟线程(Virtual Threads)

性能提升

虚拟线程是 Java 21 的一大亮点,它是轻量级线程,可以显著减少编写、维护和观察高吞吐量并发应用程序的工作量。在以往的 Java 多线程编程中,创建和管理大量线程往往面临着高开销和复杂性的问题。而虚拟线程的出现改变了这一局面,它的创建和调度成本较低,使得并发应用程序的编写变得更容易。

应用场景

虚拟线程在任务并发数高(几千以上),并且工作负载不受 CPU 限制的场景下,能够显著提高应用程序吞吐量。因为在这种情况下,拥有比处理器内核多得多的线程无法提高吞吐量,而虚拟线程可以在少量操作系统线程上运行大量任务。例如,在一个典型的服务器应用程序中,包含大量并发任务,这些任务的大部分时间都在等待,如等待数据库查询结果、网络响应等,虚拟线程就可以充分发挥其优势,大大提高应用程序的性能。

代码示例

以下是一个创建大量虚拟线程的示例程序:

ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 10000; i++) {
    executorService.submit(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    });
}
executorService.shutdown();
try {
    executorService.awaitTermination(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

在这个例子中,通过
Executors.newVirtualThreadPerTaskExecutor()创建了一个ExecutorService,它会为每个提交的任务创建一个新的虚拟线程。然后提交了 10000 个任务,每个任务只是简单地休眠一秒钟。在幕后,JDK 在少量操作系统线程上运行这些代码,可能只有一个线程,却能轻松支持 10000 个虚拟线程并发运行此类代码。

其他新特性

除了上述较为重要的新特性外,Java 21 还有一些其他值得关注的新特性:

  1. 未命名模式和变量(Unnamed Patterns and Variables):这是一个预览特性,使用未命名模式和未命名变量来增强 Java 语言。未命名模式匹配记录组件而不说明组件的名称或类型,未命名变量可以初始化但不使用,两者都用下划线字符_表示。例如在模式匹配中可以使用instanceof Point(_, int y)这样的写法,通过_来表示不需要关注的部分,提高代码的简洁性和可读性。
  2. 未命名类和实例 main 方法(Unnamed Classes and Instance Main Methods):同样是预览特性,该特性使学生无需理解为大型程序设计的语言功能即可编写第一个程序。学生们不用使用单独的 Java 方言,就可以为单类程序编写精简的声明,然后随着技能的发展,无缝地扩展程序,使用更高级的功能。比如可以定义未命名类,并且在其中使用非静态、非公共的无参数main方法作为程序入口,简化了初学者编写 Java 程序的入门门槛。
  3. 作用域值(Scoped Values):引入作用域值,这些值可以在不使用方法参数的情况下安全有效地共享给方法。它们优先于线程化局部变量,尤其是在使用大量虚拟线程时。作用域值就像是一个隐式方法参数,通过它可以在一系列调用中,将数据从调用者安全地传递给遥远的被调用者,而中间方法无需声明和访问该数据。
  4. 结构化并发(Structured Concurrency):通过引入用于结构化并发的 API 来简化并发编程。结构化并发将在不同线程中运行的相关任务组视为单个工作单元,从而简化错误处理和消除,提高可靠性,并增强可观察性。这是一个预览 API,为开发者在处理复杂并发任务时提供了更强大的工具。
  5. 弃用 Windows 32 位 x86 移植(Deprecate the Windows 32-bit x86 Port for Removal):随着技术的发展,32 位系统的使用逐渐减少,Java 21 弃用了 Windows 32 位 x86 移植,并打算在将来的版本中将其删除。
  6. 准备禁止动态加载代理(Prepare to Disallow the Dynamic Loading of Agents):当代理被动态加载到正在运行的 JVM 中时发出警告,旨在让用户为将来的版本做好准备,该版本默认情况下不允许动态加载代理,以提高默认情况下的完整性。在启动时加载代理的可服务性工具不会导致在任何版本中发出警告。
  7. 密钥封装机制 API(Key Encapsulation Mechanism API):介绍一种用于密钥封装机制(KEM)的 API,这是一种使用公钥加密来保护对称密钥的加密技术,增强了 Java 在安全领域的能力。

Java 21 的这些新特性为互联网软件开发人员带来了更多的便利和可能性。无论是提高开发效率、优化性能,还是增强代码的可读性和安全性,这些新特性都有着重要的意义。在实际开发中,我们应充分利用这些新特性,不断提升我们的开发水平和软件质量。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表