前言
1 官网下载
https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html
2 简介
官方简介: https://www.oracle.com/cn/java/
官方更新内容简介:https://openjdk.org/projects/jdk/21/
官方博客介绍:https://blogs.oracle.com/java/post/the-arrival-of-java-21
Java21 自发布日起,将会长期支持 8 年。(长期维护更新,修复bug)
相当于当年的 java8 ,预计会很受 java 程序员的喜欢。
一、Java 21 的特性 -Project Amber
Project Amber是Java和OpenJDK开发人员当前的一项举措,旨在为JDK提供一些细微但必不可少的更改,以使开发过程变得更好。 自2017年以来一直在进行,这些更新都以JEP的形式打包在一起-JDK增强建议方案。
1 字符串模板
JEP 430: String Templates (Preview)
1.1 功能描述
此功能的目的在于以下几点:
- 简化书写,使得包含在运行时计算的值得字符串更容易表达出来;
- 增强混合文本和表达式的表达式的可读性,涉及到单行字符串和多行字符串;
- 通过支持模板及其嵌入表达式的值的验证和转换,提高Java程序的安全性,这些程序从用户提供的值组成字符串并将其传递给其他系统(例如,为数据库构建查询,拼接sql语句);
- 通过允许Java库定义字符串模板中使用的格式化语法来保持灵活性;
- 简化接受用非Java语言(如SQL、XML和JSON)编写的字符串的API的使用,比如构造json模板,然后注入动态变量;
- 允许创建根据文字文本和嵌入表达式计算的非字符串值,而无需通过中间字符串表示;
1.2 原理简析
核心类是一个lang包下的接口 StringTemplate
,它里边定义了 STR
常量(以及几个类似用途的常量,这里只介绍STR),一般我们直接用这个常量来得到一个字符串即可。
该常量的定义如下:
Processor<String, RuntimeException> STR = StringTemplate::interpolate;
1.3 代码示例
最简单的用法如下:
String name = "Joan";
String info = STR."My name is \{name}";
assert "My name is Joan".equals(info); // true
int x = 10;
int y = 20;
String result = STR."\{x} + \{y} = \{x + y}";
System.out.println(result); // 10 + 20 = 30
另外,在写法上,也可以嵌入数组,方法等内容,如下示例:
// 定义一个点,拥有坐标 x,y
record Point(int x, int y) {
/**
* 计算两点间距离
*
* @param start 起点坐标
* @param end 终点坐标
* @return 距离值
*/
public static BigDecimal calcDistance(Point start, Point end) {
return BigDecimal.valueOf(Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)));
}
}
public static void main(String[] args) {
Point start = new Point(0, 0);
Point end = new Point(3, 4);
Point[] pointArray = new Point[]{start, end};
String desc = STR. "起点是\{ pointArray[0] } 终点是 \{ pointArray[1] }" ;
String startDesc = STR. "起点坐标是(\{ pointArray[0].x }, \{ pointArray[0].y })" ;
String endDesc = STR. "终点坐标是(\{ pointArray[1].x }, \{ pointArray[1].y })" ;
String multiLineDesc = STR. """
起点坐标是(\{ pointArray[0].x }, \{ pointArray[0].y })
终点坐标是(\{ pointArray[1].x }, \{ pointArray[1].y })
他们之间的距离是\{Point.calcDistance( pointArray[0], pointArray[1])}
""" ;
System.out.println(desc);
System.out.println(startDesc);
System.out.println(endDesc);
System.out.println("----multiLineDesc----");
System.out.println(multiLineDesc);
}
输出结果如下:
起点是Point[x=0, y=0] 终点是 Point[x=3, y=4]
起点坐标是(0, 0)
终点坐标是(3, 4)
----multiLineDesc----
起点坐标是(0, 0)
终点坐标是(3, 4)
他们之间的距离是5.0
另外按照这样的思路,我们可以构建 xml 、json等格式的字符串出来:
// 定义人
record People(int id, String name, int age, LocalDate birthday) {}
People people = new People(1, "小刘", 27, LocalDate.of(1996, 4, 1));
String json = STR."""
{
"id": \{people.id},
"birthday": "\{ DateTimeFormatter.ofPattern("yyyy-MM-dd").format(people.birthday)}"
}
""";
System.out.println(json);
打印结果如下:
{
"id": 1,
"birthday": "1996-04-01"
}
2 记录模式匹配
JEP 440: Record Patterns
本质上是对模式匹配的一种扩展,支持了记录类型的匹配,以及增强功能。
2.1 代码示例
早先的写法:
/**定义动物类*/
static class Animal {}
/**定义狗类,继承动物*/
static class Dog extends Animal {}
// 之前的模式匹配
Animal animal = new Dog();
if(animal instanceof Dog) {
Dog dog = (Dog) animal;
System.out.println(dog);
}
// 可定义变量名的版本
if(animal instanceof Dog dog) {
System.out.println(dog);
}
record 的模式匹配:
// 定义人
record People(int id, String name, int age, LocalDate birthday) {}
People people = new People(1, "小刘", 27, LocalDate.of(1996, 4, 1));
// record 的模式匹配
Object someone = people;
if(someone instanceof People people1) {
System.out.println(people1);
}
if(someone instanceof People(int id, String name, int age, LocalDate birthday)) {
System.out.println(STR."\{name}的生日是\{DateTimeFormatter.ofPattern("yyyy-MM-dd").format(birthday)}");
}
输出结果如下:
People[id=1, name=小刘, age=27, birthday=1996-04-01]
小刘的生日是1996-04-01
3 switch 模式匹配
JEP 441: Pattern Matching for switch
这里的switch语句变动挺大的,涉及到case null ,以及枚举,还有Object类型。也就是说case中可以更加灵活了。
3.1 代码示例 switch任意类型
public static int switchTest(Object object) {
return switch (object) {
case null -> 0;
case Integer num -> num;
case Double num -> BigDecimal.valueOf(num).intValue();
case Long num -> num.intValue();
case String str when "yes".equals(str) -> 1;
case String str when "no".equals(str) -> 0;
default -> 0;
};
}
执行结果:
System.out.println(switchTest(null)); // 0
System.out.println(switchTest(21312L)); // 21312
System.out.println(switchTest(12131)); // 12131
System.out.println(switchTest(1.2313123)); // 1
System.out.println(switchTest("yes")); // 1
System.out.println(switchTest("no")); // 0
3.2 代码示例 switch 字符串
public static int switchTestString(String str) {
return switch (str) {
case null -> 0;
case "y", "Y" -> 1;
case "n", "N" -> 0;
default -> 0;
};
}
执行结果:
System.out.println(switchTestString("y")); // 1
System.out.println(switchTestString("Y")); // 1
System.out.println(switchTestString("n")); // 0
System.out.println(switchTestString("N")); // 0
3.3 代码示例 switch 接口
示例来自官网
先定义接口,以及实现类:
// 定义接口,并指定有两个实现类
sealed interface CardClassification permits Suit, Tarot {}
public enum Suit implements CardClassification { CLUBS, DIAMONDS, HEARTS, SPADES }
static final class Tarot implements CardClassification {}
然后进行匹配:
public static void testSwitchEnum(CardClassification enumObject) {
switch (enumObject) {
case Suit s when s == Suit.CLUBS -> {
System.out.println("It's clubs");
}
case Suit s when s == Suit.DIAMONDS -> {
System.out.println("It's diamonds");
}
case Suit s when s == Suit.HEARTS -> {
System.out.println("It's hearts");
}
case Suit s -> {
System.out.println("It's spades");
}
case Tarot t -> {
System.out.println("It's a tarot");
}
}
}
执行结果:
testSwitchEnum(Suit.CLUBS); // It's clubs
testSwitchEnum(Suit.DIAMONDS); // It's diamonds
testSwitchEnum(new Tarot()); // It's a tarot
3.4 代码示例 处理null
定义处理null和不处理null的switch,进行对比。
public static void dealNullSwitch(Object object){
switch (object) {
case null -> System.out.println("null");
default -> System.out.println("default");
}
}
public static void withoutDealNullSwitch(Object object){
switch (object) {
default -> System.out.println("default");
}
}
执行结果:不处理时,会抛出空指针异常
dealNullSwitch(null); // null
withoutDealNullSwitch(null); // 抛空指针异常
4 匿名模式匹配和变量
JEP 443: Unnamed Pattern and Variable (Preview)
在使用模式匹配时,我们或许会遇到用不着的变量,但又因为语法规则,必须给他们定义出来。
现在就不必了,可以简化我们的一些操作。
其实在官方的 jep文档中,也有不少示例,我这里自己写了下我认为可能会经常用到的。
比如,可以用在for循环中,可以用在try catch 块中。
4.1 代码示例
先定义几个记录类,用于演示。
/**
* 长方形
*
* @param length 长
* @param width 宽
*/
record Rectangle(int length, int width) implements Graphical {
@Override
public BigDecimal getArea() {
return BigDecimal.valueOf((long) length * width);
}
}
/**
* 正方形
*
* @param length 边长
*/
record Square(int length) implements Graphical {
@Override
public BigDecimal getArea() {
return BigDecimal.valueOf((long) length * length);
}
}
/**
* 圆
*
* @param radius 半径
*/
record Circle(int radius) implements Graphical {
@Override
public BigDecimal getArea() {
return BigDecimal.valueOf(Math.PI * radius * radius);
}
}
/**
* 定义图形接口,并指定3个实现类
*/
sealed interface Graphical permits Rectangle, Square, Circle {
/**
* 计算面积
*
* @return 面积
*/
BigDecimal getArea();
}
然后依据以上的接口和record,使用匿名方式做验证:
public static void main(String[] args) {
Graphical g1 = new Rectangle(10, 20);
Graphical g2 = new Square(5);
Graphical g3 = new Circle(5);
Graphical[] graphicalArray = new Graphical[]{g1, g2, g3};
// switch case 模式匹配中的匿名变量
for (Graphical graphical : graphicalArray) {
switch (graphical) {
case null -> System.out.println("null");
// 不需要用到的变量,可以用下划线代替
case Rectangle(int length, _) -> System.out.println(STR."长方形的长是 \{length}");
case Circle(_) -> System.out.println(STR."圆的面积是\{graphical.getArea()}");
default -> System.out.println("default");
}
}
// instanceof 时的匿名变量
if(g1 instanceof Rectangle(_, int width)) {
System.out.println(STR."长方形的宽是\{width}, 面积是\{g1.getArea()}");
}
// for循环中的匿名变量
int graphicalCount = 0;
for (Graphical _ : graphicalArray) {
graphicalCount ++;
}
}
5 匿名类和实例main方法
JEP 445: Unnamed Classes and Instance Main Methods (Preview)
这是一个预览的功能,默认禁用。其实就是简化了下main方法,实际开发没啥大用。
void main() {
System.out.println("Hello, World!");
}
PS :我本地实验的时候,发现idea即使最新版,也不能直接执行它。就放弃了。
二、Java 21 的特性 -Project Loom
探索新的Java语言特性、API和轻量级并发的运行时-包括虚拟线程的新构造
1 虚拟线程
JEP 444: Virtual Threads
现在的虚拟线程可以通过线程框架来创建了, 并且支持了 threadlocal变量。
Executors.newVirtualThreadPerTaskExecutor()
1.1 基本概念
在jep的描述中,有这样一段话:
Today, every instance of java.lang.Thread in the JDK is a platform thread.
A platform thread runs Java code on an underlying OS thread and captures the OS thread for the code's entire lifetime.
The number of platform threads is limited to the number of OS threads.
A virtual thread is an instance of java.lang.Thread that runs Java code on an underlying OS thread but does not capture the OS thread for the code's entire lifetime. This means that many virtual threads can run their Java code on the same OS thread, effectively sharing it.
While a platform thread monopolizes a precious OS thread, a virtual thread does not.
The number of virtual threads can be much larger than the number of OS threads.
Virtual threads are a lightweight implementation of threads that is provided by the JDK rather than the OS. They are a form of user-mode threads, which have been successful in other multithreaded languages (e.g., goroutines in Go and processes in Erlang).
User-mode threads even featured as so-
大致含义是:
如今,JDK中java.lang.Thread的每个实例都是一个平台线程。平台线程在底层操作系统线程上运行Java代码,并在代码的整个生命周期中捕获操作系统线程。平台线程的数量限制为操作系统线程的数量。
虚拟线程是java.lang.thread的一个实例,它在底层操作系统线程上运行java代码,但在代码的整个生命周期内不会捕获该操作系统线程。这意味着许多虚拟线程可以在同一个操作系统线程上运行Java代码,从而有效地共享代码。虽然平台线程独占了宝贵的操作系统线程,但虚拟线程却没有。虚拟线程的数量可以比操作系统线程的数量大得多。
虚拟线程是JDK而不是OS提供的线程的轻量级实现。它们是用户模式线程的一种形式,在其他多线程语言中也很成功(例如Go中的goroutines和Erlang中的processes)。用户模式线程也是如此。
官方建议,虚拟线程不要被池化。
1.2 Thread API 的更新
-
创建虚拟线程:
Thread.Builder, Thread.ofVirtual(), and Thread.ofPlatform()
比如:Thread thread = Thread.ofVirtual().name("duke").unstarted(runnable);
-
创建并启动虚拟线程:
Thread.startVirtualThread(Runnable)
Thread.Builder
可以建造线程或线程工厂。 -
判断线程是否是虚拟线程:
Thread.isVirtual()
1.3 代码示例
这个在使用时,写的代码比较简单,我这里直接粘贴了官方文档中的示例:
void handle(Request request, Response response) {
var url1 = ...
var url2 = ...
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future1 = executor.submit(() -> fetchURL(url1));
var future2 = executor.submit(() -> fetchURL(url2));
response.send(future1.get() + future2.get());
} catch (ExecutionException | InterruptedException e) {
response.fail(e);
}
}
String fetchURL(URL url) throws IOException {
try (var in = url.openStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
}
2 作用域值
JEP 446: Scoped Values (Preview)
待完善
3 结构化并发
JEP 453: Structured Concurrency (Preview)
三、Java 21 的特性 -Project Panama
主要涉及Java与外部api的交互。比如C语言。
这一部分本人目前为止没咋用过,就不研究了,脑容量有限。
四、Java 21 的特性 -核心库
1 序列化集合
JEP 431: Sequenced Collections
五、Java 21 的特性 -性能更新
1 分代 ZGC
JEP 439: Generational ZGC
2 密钥封装机制api
JEP 452: Key Encapsulation Mechanism API
六、维护和弃用
1 弃用Windows 32位x86端口
JEP 449: Deprecate the Windows 32-bit x86 Port for Removal文章来源:https://www.toymoban.com/news/detail-783554.html
2 禁止动态加载代理
JEP 451: Prepare to Disallow the Dynamic Loading of Agents文章来源地址https://www.toymoban.com/news/detail-783554.html
到了这里,关于Java21来了,赶紧用起来(附代码使用示例)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!