Java 21 新功能展示(含示例)

news2024/10/7 14:25:05

Java 21 于 2023 年 9 月 19 日发布,是 Oracle 标准 Java 实现的下一个长期支持(LTS)版本。Java 21 具有以下 15 项功能。

字符串模板(预览版) [JEP-430] 
序列集合 [JEP-431] 
代 ZGC [JEP-439]
记录模式 [JEP-440]
开关的模式匹配 [JEP-441]
外来函数和内存 API(第三次预览) [JEP-442]
未命名模式和变量(预览) [JEP-443] 
虚拟线程 [JEP-444] 
未命名类和实例主方法(预览) [JEP-445] 
作用域值(预览) [JEP-446] 
矢量 API(第六期孵化器) [JEP-448]
停用 Windows 32 位 x86 端口,以便删除 [JEP-449]
准备禁止动态加载代理 [JEP-451]
密钥封装机制 API [JEP-452]
结构化并发(预览版) [JEP-453]

1.虚拟线程(Project Loom)

虚拟线程是由 JVM 管理的轻量级线程,有助于编写高吞吐量并发应用程序(吞吐量指系统在给定时间内能处理多少个信息单位)。[JEP-425、JEP-436 和 JEP-444]在 Java 21 中,虚拟线程已可投入生产使用。

引入虚拟线程后,只需使用几个操作系统线程就能执行数百万个虚拟线程。最有利的一点是,无需修改现有的 Java 代码。只需指示我们的应用程序框架使用虚拟线程来代替平台线程即可。

要使用 Java API 创建虚拟线程,我们可以使用线程或执行器。

Runnable runnable = () -> System.out.println("Inside Runnable");
//1
Thread.startVirtualThread(runnable);
//2
Thread virtualThread = Thread.ofVirtual().start(runnable);
//3
var executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(runnable);

请注意,虚拟线程并不比平台线程更快。虚拟线程应该用于扩展大部分时间都在等待的并发任务的数量。例如,处理大量客户端请求和执行阻塞 I/O 操作的服务器应用程序。对于资源/处理密集型任务,应继续使用传统的平台线程,因为虚拟线程不会带来任何优势。

此外,要注意的是,更多的线程意味着依赖更多的系统资源,而这些资源可能无法按比例扩展。为了防止资源耗尽并确保最佳的系统利用率,我们必须使用 Semaphore 等机制限制并发线程的数量来进行测试。

2.Sequenced Collections

在有序集合计划下创建的新界面表示具有定义的相遇顺序的集合。该顺序将有明确定义的第一个元素、第二个元素,依此类推,直到最后一个元素。新添加的接口提供了统一的应用程序接口,可按顺序或相反顺序访问这些元素。

现在,所有流行和常用的集合类都根据相应的集合类型实现了 java.util.SequencedCollection、java.util.SequencedSet 或 java.util.SequencedMap 接口。

新的接口有额外的方法来支持对元素的顺序访问。例如,SequencedCollection 有以下方法:

interface SequencedCollection<E> extends Collection<E> {

  // new method
  SequencedCollection<E> reversed();

  // methods promoted from Deque
  void addFirst(E);
  void addLast(E);
  E getFirst();
  E getLast();
  E removeFirst();
  E removeLast();
}

请注意,对原始集合的任何修改在反转集合视图中都是可见的。让我们通过一个 Java 程序来了解如何使用新的有序 ArrayList:

ArrayList<Integer> arrayList = new ArrayList<>();
addFirst.add(1); 				// [1]
arrayList.addFirst(0);	// [0, 1]
arrayList.addLast(2);		// [0, 1, 2]
arrayList.getFirst();	// 0
arrayList.getLast();	// 2

要了解其优势,请参阅 Java 17 中这些简单操作如何过于繁琐。

arrayList.get( arrayList.iterator().next() ); // first element
arrayList.get( arrayList.size() - 1 ); // last element

3.Record Patterns

Java 中的record是透明且不可更改的数据载体(类似于 POJO)。我们创建记录的过程如下:

record Point(int x, int y) {}

以前,如果我们需要访问记录的组件,应按如下方式对其进行重组:

if (obj instanceof Point p) {

  int x = p.x();
  int y = p.y();
  System.out.println(x+y);
}

在 Java 21 中,我们可以使用 Point(int x, int y)语法(称为记录模式)以更简洁的方式重写它。

记录模式省去了为提取的组件声明局部变量的过程,当一个值与模式匹配时,通过调用访问器方法来初始化组件。

if (obj instanceof Point(int x, int y)) {

    System.out.println(x+y);
}

4.Pattern Matching for switch

从 Java 21 开始,我们可以在 switch 语句中使用记录模式。请注意,switch 块必须包含处理选择表达式所有可能值的子句。

例如,在 Java 16 中,我们可以这样做

record Point(int x, int y) {}
public void print(Object o) {
  switch (o) {
    case Point p 		-> System.out.printf("o is a position: %d/%d%n", p.x(), p.y());
    case String s   -> System.out.printf("o is a string: %s%n", s);
    default         -> System.out.printf("o is something else: %s%n", o);
  }
}

在 Java 21 中,我们可以用记录模式写出类似的表达式,如下所示:

public void print(Object o) {

  switch (o) {

    case Point(int x, int y) 		-> System.out.printf("o is a position: %d/%d%n", x, y);
    case String s               -> System.out.printf("o is a string: %s%n", s);
    default                     -> System.out.printf("o is something else: %s%n", o);
  }
}

5.String Templates (Preview)

使用字符串模板,我们可以创建包含嵌入式表达式(运行时求值)的字符串模板。模板字符串可以包含变量、方法或字段,在运行时进行计算,生成格式化的字符串作为输出。

从语法上讲,模板表达式类似于带前缀的字符串字面量。

let message = "Greetings {{ name }}!";  			//TypeScript
String message = STR."Greetings \{ name }!";  //Java

在上述模板表达式中

STR 是模板处理器。
在处理器和表达式之间有一个点运算符(.)。
内嵌表达式的模板字符串。表达式的形式是 (\{name})。
请注意,模板处理器的结果以及模板表达式的求值结果通常是字符串,但不一定总是字符串。

6.未命名模式和变量(预览)

在其他一些编程语言(如 Scala 和 Python)中,我们可以跳过对将来不会使用的变量的命名。现在,从 Java 21 开始,我们也可以在 Java 中使用未命名/未使用的变量了。

让我们通过一个例子来理解。我们通常会如下处理异常:

String s = ...;
try {
    int i = Integer.parseInt(s);
    //use i
} catch (NumberFormatException ex) {
    System.out.println("Invalid number: " + s);
}

请注意,我们创建了变量 ex,但没有在任何地方使用它。在上例中,变量未被使用,其名称也无关紧要。未命名变量的特性允许我们跳过变量的名称,而直接使用下划线 (_) 来代替它。下划线表示没有变量名。

String s = ...;
try {
    int i = Integer.parseInt(s);
    //use i
} catch (NumberFormatException _) {
    System.out.println("Invalid number: " + s);
}

同样,我们也可以在开关表达式中使用未命名变量:

switch (obj) {
  case Byte, Short, Integer, Long   _ -> System.out.println("Input is a number");
  case Float, Double  _ -> System.out.println("Input is a floating-point number");
  case String _ -> System.out.println("Input is a string");
  default -> System.out.println("Object type not expected");
}

有了未命名模式,我们还可以在记录模式中使用未命名变量。让我们将前面的例子中用于开关表达式的记录模式与未命名变量混合使用,从而产生未命名模式。

在下面的示例中,我们不使用 y 的值,因此可以简单地将其声明为未命名变量。

public void print(Object o) {
  switch (o) {
    case Point(int x, int _) 		-> System.out.printf("The x position is : %d%n", x);  // Prints only x
    //...
  }
}

7.未命名类和实例主方法(预览)

这是预览语言功能,默认情况下是禁用的。要使用它,我们必须使用 --enable-preview 标志启用预览功能。

在 Java 中,未命名模块和包是一个熟悉的概念。如果我们不创建 module-info.java 类,编译器就会自动假定该模块。同样,如果我们不在根目录下的类中添加包语句,类就会被编译并正常运行。

同样,我们现在可以创建未命名的类。很明显,未命名类就是没有名称的类。

请看下面的类声明,我们通常创建它来测试代码片段或一个简单的概念。

public class TestAConcept {
  public static void main(String[] args) {
    System.out.println(method());
  }
  static String method() {
  	//...
  }
}

在 Java 21 中,我们可以不使用类声明来编写上述类,如下所示。它删除了类声明、public 和 static 访问修饰符等,使类更加简洁。

void main(String[] args) {
  System.out.println(method());
}
String method() {
	//...
}

在 Java 21 中,我们可以使用命令创建上述类。请注意,我们已将该类保存在 TestAConcept.java 文件中。

$ java --enable-preview --source 21 TestAConcept.java

8.Scoped Values(预览)

如果你熟悉 ThreadLocal 变量,那么作用域值就是一种在线程内和线程间共享数据的现代方式。作用域值允许在有限的时间内存储一个值(对象),只有写入该值的线程才能读取该值。

作用域值通常创建为公共静态字段,因此我们可以直接访问它们,而无需将其作为参数传递给任何方法。但是,我们必须明白,如果该值在多个方法中被检查,那么当前值将取决于线程的执行时间和状态。在不同方法中访问时,该值可能会随时间而改变。

要创建作用域值,请使用 ScopedValue.newInstance() 工厂方法。

public final static ScopedValue<USER> LOGGED_IN_USER = ScopedValue.newInstance();

通过 ScopedValue.where(),我们将作用域值与对象实例绑定,然后运行一个方法,在该方法的调用持续时间内,作用域值应该有效。请注意,作用域值只写入一次,然后不可更改,因此任何人都无法在调用的方法中更改登录用户。

class LoginUtil {
	public final static ScopedValue<USER> LOGGED_IN_USER = ScopedValue.newInstance();
	//Inside some method
	User loggedInUser = authenticateUser(request);
	ScopedValue.where(LOGGED_IN_USER, loggedInUser).run(() -> service.getData());
}

在被调用的线程中,我们可以直接访问作用域值:

public void getData() {
	User loggedInUser = LoginUtil.LOGGED_IN_USER.get();
	//use loggedInUser
}

9.结构化并发(预览)

结构化并发功能旨在将运行在不同线程(分叉自同一父线程)中的多个任务视为一个工作单元,从而简化 Java 并发程序。将所有此类子线程视为单一工作单元有助于将所有线程作为一个单元进行管理,从而更可靠地进行取消和错误处理。

在结构化多线程代码中,如果一个任务分成多个并发子任务,它们都会返回到同一个地方,即任务的代码块。这样,并发子任务的生命周期就仅限于该语法块。

在这种方法中,子任务代表任务工作,而任务则等待子任务的结果并监控子任务是否出现故障。在运行时,结构化并发会建立一个树形的任务层次结构,同级子任务归属于同一个父任务。这棵树可以看作是单线程调用堆栈的并发对应物,其中有多个方法调用。

try (var scope = new StructuredTaskScope.ShutdownOnFailure()()) {
    Future<AccountDetails> accountDetailsFuture = scope.fork(() -> getAccountDetails(id));
    Future<LinkedAccounts> linkedAccountsFuture = scope.fork(() -> fetchLinkedAccounts(id));
    Future<DemographicData> userDetailsFuture = scope.fork(() -> fetchUserDetails(id));
    scope.join();	// Join all subtasks
    scope.throwIfFailed(e -> new WebApplicationException(e));
    //The subtasks have completed by now so process the result
    return new Response(accountDetailsFuture.resultNow(),
	    	linkedAccountsFuture.resultNow(),
	    	userDetailsFuture.resultNow());
}

翻译自:Java 21 Features: Practical Examples and Insights

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1065907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Helm部署EMQX集群

端口说明&#xff1a; 端口说明集群内端口Nodeport备注mqtt188331274Port for MQTTmqttssl888331170Port for MQTT(SSL)mgmt8081无ws808330099Port for WebSocket/HTTPwss808432580Port for WSS/HTTPSdashboard1808331303Port for dashboard 一、Helm在线部署EMQX集群 1、安…

Compose输入框

TextField label设置提示内容&#xff0c;TextFieldDefaults.textFieldColors设置输入框背景颜色 TextField(modifier Modifier.fillMaxWidth().padding(5.dp),value text.value,onValueChange { text.value it },label { Text(text "请输入内容") },colors …

算法题:买卖股票的最佳时机 II

这道题是贪心算法的中级难度练习题&#xff0c;由于题目设定&#xff0c;整个价格都是透明的&#xff0c;这里并不涉及需要预测股票涨势的问题。解决思路不难&#xff0c;就是一旦股票价格开始下降了就买入&#xff0c;一旦上升了&#xff0c;就赶紧卖出。&#xff08;完整题目…

led护眼台灯对眼睛有伤害吗?推荐好用的led护眼台灯

其实led护眼台灯对眼睛伤害是不大的&#xff0c;而且和白炽灯、卤素灯等老式台灯相对比&#xff0c;反而更加护眼。因为白炽灯、卤素灯等光线都不太稳定&#xff0c;而且光线不是很均匀可以明显感觉有明暗差&#xff0c;最主要的是频闪现象会比较严重&#xff0c;长时间使用的话…

Apache Tomcat安装、运行

介绍 Apache Tomcat是下面多个规范的一个开源实现&#xff1a;Jakarta Servlet、Jakarta Server Pages、Jakarta Expression Language、Jakarta WebSocket、Jakarta Annotations 和 Jakarta Authentication。这些规范是 Jakarta EE 平台的一部分。 Jakarta EE 平台是Java EE平…

Vue Router的使用

使用 项目中注入路由器 在项目中 src 目录下新建 router 目录&#xff0c;其中包含 index.js 路由主文件。 // src/router/index.jsimport Vue from vue import Router from vue-router import { routes } from ./routes.jsVue.use(Router) const router new Router({route…

EPDB 08、EPDBS 10、PDRV、EPDZA06插装式比例减压阀放大器

比例方向阀W42E-5PS03、W43E-5PS05、W42E-1AS06、W43E-1AS06、插装式S22E-1V08放大器。 该放大器既可用于工业及移动设备应用&#xff0c;也可用于固定安装。因此&#xff0c;电压范围非常宽&#xff0c;在8至35VDC之间变化。这些放大器对欠压和电压尖峰非常不敏感&#xff0c…

浅谈时间流管理体系

不想聊技术&#xff0c;但又想分享一些东西&#xff0c;这篇文章分享下如何构造自己的时间流管理体系以及如何完整的把控一个事件安排统筹&#xff0c;这里对一个大型事件或大型知识体系如何分解为不同问题的小点不做点出&#xff0c;这里只提时间管理体系化。 好处的话也不做阐…

Nginx+Keepalived实现服务高可用

Nginx 和 Keepalived 是常用于构建高可用性&#xff08;High Availability&#xff09;架构的工具。Nginx 是一款高性能的Web服务器和反向代理服务器&#xff0c;而Keepalived则提供了对Nginx服务的健康状态监测和故障切换功能。 下载Nginx 在服务器1和服务器2分别下载nginx …

深入理解 python 虚拟机:原来虚拟机是这么实现闭包的

深入理解 python 虚拟机&#xff1a;原来虚拟机是这么实现闭包的 在本篇文章当中主要从虚拟机层面讨论函数闭包是如何实现的&#xff0c;当能够从设计者的层面去理解闭包就再也不用死记硬背一些闭包的概念了&#xff0c;因为如果你理解闭包的设计原理之后&#xff0c;这些都是…

嵌入式学习笔记(48)什么是I2C通信

10.1.1物理接口&#xff1a;SCL SDA (1)SCL&#xff1a;时钟线&#xff0c;传输CLK&#xff0c;一般是I2C主设备向从设备提供时钟的通道。 (2)SDA&#xff1a;数据线&#xff0c;通信数据都通过SDA线传输。 10.1.2通信特征&#xff1a;串行、同步、非差分、低速 (1)I2C属于…

Python中的多态

迷途小书童 读完需要 3分钟 速读仅需 1 分钟 当我们谈到多态时&#xff0c;可以将其比喻为一个人具有多种身份的能力。在不同的情境下&#xff0c;这个人可以表现出不同的行为和特征。在 Python 中&#xff0c;多态是面向对象编程中的一个重要概念&#xff0c;它允许我们使用相…

PHP 伪协议:使用 php://filter 为数据流应用过滤器

文章目录 参考环境PHP 伪协议概念为什么需要 PHP 伪协议&#xff1f; php://filter概念格式 基本使用普通读写file_get_contents 与 file_put_contentsinclude 过滤器的基本使用base64 的编码与解码rot13 加解密rot13 算法string.rot13 过滤器列表多个过滤器的使用注意事项 处理…

【软件测试】功能测试/接口测试/自动化测试/性能测试/验收测试

软件测试的主要流程 一、测试主要的四个阶段 1.测试计划设计阶段&#xff1a;产品立项之后&#xff0c;进行需求分析&#xff0c;需求评审&#xff0c;业务需求评级&#xff0c;绘制业务流程图。确定测试负责人&#xff0c;开始制定测试计划&#xff1b; 2.测试准备阶段&…

【每日一题】股票价格跨度

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;暴力枚举方法二&#xff1a;单调栈 写在最后 Tag 【单调栈】【设计类】【数组】【2023-10-07】 题目来源 901. 股票价格跨度 题目解读 找出小于等于今天股票价格的最大连续天数&#xff08;从今天往回数&#xff0c;…

AI颠覆法律行业,律师要失业了?

如果要说一个 AI 真正起飞&#xff0c;并且对行业从业者带来的更多是正面影响的垂直行业&#xff0c;小编觉得在目前阶段&#xff0c;法律可以算一个。这个行业有几个特点&#xff1a;对人的依赖很大&#xff0c;专业性很强&#xff0c;大量繁复的文字工作。因此&#xff0c;在…

水土保持方案编制丨点型项目、市政工程、线型工程、矿山工程、水利工程、取土场/弃渣场、补报项目、水土保持监测验收等

目录 专题一 点型水土保持方案编制方法及案例分析 专题二 市政工程水土保持方案编制方法及案例分析 专题三 线型工程水土保持方案编制方法及案例分析 专题四 矿山工程水土保持方案编制方法及案例分析 专题五 水利工程水土保持方案编制方法及案例分析 专题六 取土场、弃渣…

电影产业的数据洞察:爬虫技术在票房分析中的应用

概述 电影产业是一个庞大而复杂的行业&#xff0c;涉及到各种各样的因素&#xff0c;如导演、演员、类型、主题、预算、宣传、口碑、评分、奖项等。这些因素都会影响电影的票房收入&#xff0c;也会反映出电影市场的动态和趋势。为了更好地了解电影产业的数据洞察&#xff0c;…

Python机器学习实战-特征重要性分析方法(6):XGBoost(附源码和实现效果)

实现功能 计算一个特性用于跨所有树拆分数据的次数。更多的分裂意味着更重要。 实现代码 import xgboost as xgb import pandas as pd from sklearn.datasets import load_breast_cancer import matplotlib.pyplot as pltX, y load_breast_cancer(return_X_yTrue) df pd.D…