文章目录
1. 概述
2023年9月19日 ,Oracle 发布了 JDK21,是自 JDK17 之后最新的 LTS 版本(long-term support,长期支持版)。LTS版本一般每两年发布一个,JDK目前的LTS版本有:JDK8 , JDK11 , JDK17 ,JDK21。
Java21新特性:( oracle jdk、openjdk文档)
- 字符串模板(预览版)
- 虚拟线程(在JDK19中是预览版,在JDK21中是正式版,会大大改变多线程的编程风格和解决问题的方式)
- 顺序集合(JDK21引入了SequencedCollection、SequencedSet 、SequencedMap 接口,这些接口提供了在集合开头或结尾处添加、修改或删除元素的方法,以及反转集合的功能)
- 分代ZGC(属于JVM层面的功能)
- 记录模式
- switch的模式匹配
- 未命名模式和变量(预览版)
- 未命名类和实例的main方法(预览版)
- 作用域值(预览版)
- 矢量API(第六个孵化器)
- 弃用Windows32位x86端口
- 禁止代理的动态加载
- 密钥封装机制的API(引入密钥封装机制 (KEM) 的 API,是一种使用公钥加密来保护对称密钥的加密技术)
- 结构化并发(预览版, 简化并发编程)
- 外部函数与内存API(第三次预览版)
2. JDK21 安装与配置
Oracle JDK21 官网下载地址:https://www.oracle.com/java/technologies/downloads/#java21
1、下载 windows 版 oracle jdk,下载地址:https://download.oracle.com/java/21/latest/jdk-21_windows-x64_bin.exe
2、双击刚刚下载好的jdk安装包,进行安装:
3、配置环境变量,在系统变量path中创建新的变量值,变量值的内容就是dk21的安装位置:(偷懒了😈)
系统变量 | 变量值 |
---|---|
Path | D:\Program Files\Environment\Java\jdk-21 |
3. 新特性
3.1 switch模式匹配
在Java8中switch可以处理的数据类型有:byte、short、char、int、String、Enum。
而在Java21中switch可以处理更多的类型,会根据类型进行匹配,switch增强版语法如下:
switch(object){
case 普通类型 变量名 [when 条件] -> ...; //通过when关键字可以进行条件筛选,[]表示可选语法
case record 类型 变量名 -> ...; // record 记录
case enum 类型 变量名 -> ...;
case null -> ...;
case default -> ...;
}
示例:
package cn.z3inc.jdk21;
import java.time.LocalDate;
/**
* switch 模式匹配
*
* @author 白豆五
* @date 2023/9/29
* @since JDK21
*/
public class SwitchTest {
public static void main(String[] args) {
test1(new A("HUAWEI Mate 60 Pro"));
test2(new B("遥遥领先"));
test2(null);
test3();
}
/**
* 根据不同的类型去做不同的事情 (jdk21之前的写法)
*
* @param obj 入参的类型
*/
static void test1(Object obj) {
if (obj instanceof A a) { //instanceof 的模式匹配(Java14的新语法,老版本需要强转)
System.out.println("类型A===" + a.name);
} else if (obj instanceof B b) {
System.out.println("类型B===" + b.title);
}
}
/**
* 根据不同的类型去做不同的事情 (jdk21:switch的新语法)
*
* @param obj 入参的类型
*/
static void test2(Object obj) {
switch (obj) {
// case 数据类型 变量名 -> 表达式;
case A a -> System.out.println("类型A===" + a.name);
case B b -> System.out.println("类型B===" + b.title);
// case null -> System.out.println("null");
// default -> System.out.println("其他类型");
case null, default -> System.out.println("其他类型或null"); // 合并写法
}
}
// 静态内部类A
static class A {
String name;
public A(String name) {
this.name = name;
}
}
// 静态内部类B
static class B {
String title;
public B(String title) {
this.title = title;
}
}
/**
* 输出当前日期格式为"yyyy-MM-dd"
*/
static void test3() {
LocalDate date = LocalDate.now();
System.out.println("date = " + date);
String formatted = switch (date.getDayOfWeek()) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "工作日";
case SATURDAY, SUNDAY -> "周末";
};
System.out.println(formatted);
}
}
3.2 字符串模板
在Java21之前拼接字符串的写法如下:
方式1:使用 +
拼接字符串
String x = "world";
String s = "hello" + x + "Java21!";
方式2:使用StringBuilder拼接字符串
String s = new StringBuilder()
.append("Hello")
.append(" ")
.append("World!")
.toString();
方式3:使用String的format()或formatted()方法,格式化字符串(有点像c语言的写法)
String name = "旺财";
int age = 18;
char sex = '男';
// 1. String.format():用于格式化字符串
// 参数1: 一个字符串模板 里面包含占位符%xxx"
// 参数2: 替换的值 可变参数
String s = String.format("姓名 %s,年龄 %d,性别 %c", name, age, sex);
System.out.println(s); // 姓名 旺财,年龄 18,性别 男
// 2. String.formatted()是Java15新特性,与String.format()功能一样
s = "姓名 %s,年龄 %d,性别 %c".formatted(name, age, sex);
System.out.println(s); // 姓名 旺财,年龄 18,性别 男
/*
占位符:
%s字符串 %d整数 %f浮点数 %c字符 %b布尔值 %t时间
%e科学计数法 %x十六进制 %o八进制 %h哈希码 %n换行
*/
在Java21中引入字符串模板,在字符串模板中允许在字符串中插入变量或表达式,而无需使用运算符 “+” 拼接字符串,这样可以使字符串拼接操作更加直观和方便。
STR模板处理器:STR是Java平台中的一种模板处理器,用于执行字符串的插值处理。通过STR可以将模板中的占位符或表达式动态替换为对应的值,生成最终的字符串输出。
STR是一个公共静态final字段,会自动导入到每个Java源文件中。
示例:字符串模板快速入门
String s1 = "hello";
String s2 = "world";
String target = STR."\{s1} \{s2}!"; //在字符串模板中使用`\{}`来引用变量
System.out.println(target); // hello world!
target = STR."\{s2}\{s1}";
System.out.println(target);// worldhello
示例:打印99乘法表
package cn.z3inc.jdk21;
import static java.lang.StringTemplate.STR;
public class StringTemplateTest {
public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
for (int j = 1; j < 10; j++) {
if (j > i) {
break;
}
System.out.print(STR."\{j} * \{i} = \{i * j} \t");
}
System.out.println();
}
}
}
示例:字符串中嵌入表达式(模板嵌套多了就不太直观了,可以分开写,拒绝套娃😉)
package cn.z3inc.jdk21;
import static java.lang.StringTemplate.STR;
public class StringTemplateTest {
public static void main(String[] args) {
String name = "白豆五";
String str = STR. "\{
isNotBlank(name) ? STR. "\{ name }签到成功" : "签到失败"
} " ;
System.out.println(str); //白豆五签到成功
}
/**
* 字符串非空判断
*
* @return 空串返回fale,否则返回true
*/
public static boolean isNotBlank(String str) {
return !(str == null || str.length() == 0);
}
}
示例:多行模板表达式(类似python中的多行字符串输出)
package cn.z3inc.jdk21;
import static java.lang.StringTemplate.STR;
public class StringTemplateTest {
public static void main(String[] args) {
String title = "假装404";
String text = "Hello, world";
// 3个双引号代表多行字符串
String html = STR. """
<html>
<head>
<title>\{ title }</title>
</head>
<body>
<p>\{ text }</p>
</body>
</html>
""" ;
System.out.println(html);
}
}
3.3 顺序集合
在Java21中引入了全新的SequencedCollection、SequencedSet 、SequencedMap 接口,来表示顺序集合,这些接口提供了在集合开头或结尾处添加、修改或删除元素的方法,以及反转集合的功能。
Java21集合体系结构:
示例:
package cn.z3inc.jdk21;
import java.util.ArrayList;
import java.util.SequencedCollection;
public class SequencedCollectionsTest {
public static void main(String[] args) {
SequencedCollection<String> list = new ArrayList<>();
list.add("b");
list.addLast("c");
list.addFirst("a");
// 倒序遍历
for (String s : list.reversed()) {
System.out.println(s);
}
}
}
3.4 记录模式(Record Patterns)
类似JS中的对象参数解构赋值,把Java中的Record对象拆解成多个变量。
Record类适用场景:
- 作为数据载体,如 DTO,VO等
- 相对于属性可变的普通pojo,Record对象更适合函数式编程。
示例:Record 快速入门
package cn.z3inc.jdk21;
// 老师类 record类型
public record Teacher(String name, int age) {
}
/*
//record类的源码:
public final class Teacher extends java.lang.Record {
// 字段初始化后不可变
private final String name;
private final int age;
// 带参构造器
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
//提供两个get方法
public String name() {
return name;
}
public int age() {
return age;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Teacher) obj;
return Objects.equals(this.name, that.name) &&
this.age == that.age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Teacher[" +
"name=" + name + ", " +
"age=" + age + ']';
}
}
*/
package cn.z3inc.jdk21;
/**
* 测试 记录模式
* @author 白豆五
* @date 2023/9/30
* @since JDK21
*/
public class RecordTest {
public static void main(String[] args) {
Teacher teacher = new Teacher("张三", 18);
System.out.println(teacher);
System.out.println("name:"+teacher.name());
System.out.println("age:"+teacher.age());
System.out.println("equals:"+teacher.equals(new Teacher("张三", 18)));
System.out.println("hashCode:"+teacher.hashCode());
System.out.println("toString:"+teacher.toString());
}
}
示例2:
package cn.z3inc.jdk21;
// 老师类 记录类型
public record Teacher(String name, int age) {
}
package cn.z3inc.jdk21;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 需求: 把老师数据转成java对象,保留20岁以上的老师,姓相同的分到一组
*
* @author 白豆五
* @date 2023/9/30
* @since JDK21
*/
public class RecordTest2 {
public static void main(String[] args) {
// 1. 模拟excel表格数据
String cvs = """
张三,23
李四,20
王五,19
赵六,18
张富贵,22
李狗蛋,15
""";
// 2. 按行切割字符串
String[] splitArr = cvs.split("\n");
// 3. 通过stream流处理数据
Map<Character, List<Teacher>> map = Arrays.stream(splitArr)
// 3.1. 把每一行数据转成Teacher对象
.map(line -> {
String[] split = line.split(",");
return new Teacher(split[0], Integer.parseInt(split[1]));
})
// 3.2. 过滤掉年龄小于20岁的老师
.filter(teacher -> teacher.age() >= 20)
// 3.3. 按姓氏分组
.collect(Collectors.groupingBy(teacher -> teacher.name().charAt(0)));
System.out.println(map);
}
}
示例3:
package cn.z3inc.jdk21;
/**
* 测试 记录模式
* @author 白豆五
* @date 2023/9/30
* @since JDK21
*/
public class RecordTest3 {
public static void main(String[] args) {
print2(new Teacher("张三", 23));
}
/**
* 传统方式写法
*
* @param o
*/
@Deprecated
static void print(Object o) {
if (o instanceof Teacher) {
Teacher t = (Teacher) o;
System.out.println("name:" + t.name() + " ,age:" + t.age());
}
}
/**
* 记录模式写法
* @param o
*/
static void print2(Object o) {
if (o instanceof Teacher(String name, int age)) { //解构记录对象中的值
System.out.println("name:" + name + " ,age:" + age);
}
}
}
3.5 未命名类和实例的main方法(预览版)
可以写类名,也可以不写类名。
编译:
javac --release 21 --enable-preview MainTest.java
运行:
java --enable-preview MainTest
3.6 虚拟线程
todo