目录
1 背景
New Relic发布的《2023 State of the Java Ecosystem》报告显示目前最受欢迎的Java版本是 2018 年 9 月发布的 Java 11,有超过 56% 的应用程序在生产中使用它,高于 2022 年的 48% 和 2020 年的 11% 占比。2014 年发布的 Java 8 紧随其后,近 33% 的应用程序在生产中使用该版本,低于 2022 年的 46%。报告还显示,虽然Java11已经连续两年霸占榜单,但Java17的采用率增长速度远远超过同时期的Java11。截止2023年已有超过9%的应用程序在生产中使用Java11,远高于2022年同时期的1%,一年内增长率为430%,而Java11花了数年才达到这个水平。
图1:2020-2023年Java LTS版本采用率
Spring 系列框架在Java开发领域中被广为使用,具有灵活、高效、快速、安全等特点,用户规模大并且开发生态完整。而Spring Framework 6.0和Spring Boot3.0版本最低支持的都是Java 17。
2 JDK新特性
图2-1展示的是JDK8至JDK21每个版本新增的JDK改进提案(JEPs,JDK Enhancement Proposals)数量,JDK8和JDK9新增数量远多余后续版本JDK,JDK9之后的JDK版本每次新增JEPs数量均在20以下。值得注意的是JDK8、JDK11、JDK17和JDK21为Oracle官方长期维护版本,其余的JDK版本并非长期维护,不建议应用于实际生产中。从JDK8到21的迭代过程大致可以划分为三个阶段,分别为JDK9到JDK11,JDK12至JDK17及JDK18至JDK21。
图2:JDK8-JDK21新增JEPs数量
2.1 JDK8到JDK11新特性
2.1.1 JDK9新特性
-
集合工厂方法的不可变集合
不可变集合是指那些一旦创建就不能修改的集合,其可以有效防止集合中数据被污染及提高代码的安全性及可维护性,并且由于不能被修改,因而该集合是线程安全的,可以同时被多个线程所访问。JDK9中以在集合接口上提供静态工厂方法的形式提供了一种新的不可变集合实例的创建方式。在此之前的不可变集合是通过以下形式来创建的:
List<String> immutableList = Collections.unmodifiableList(new ArrayList<>());
Set<Integer> immutableSet = Collections.unmodifiableSet(new HashSet<>());
Map<String, Integer> immutableMap = Collections.unmodifiableMap(new HashMap<>());
//集合元素添加方式:Arrays.asList("a", "b", "c");Stream.of("a", "b","c").collect(toSet())
可以发现上述创建方式极为冗长,为此在JDK9中提供了更为简洁小型不可变集合的创建形式,如下所示:
Set<String> set = Set.of("a", "b", "c");
List.of(a, b, c);
//该Map创建方法支持最多10个键值对映射,足够覆盖大部分用例
Map.of(k1, v1, k2, v2)
Map.of(k1, v1, k2, v2, k3, v3)
//对于数目较多的键值对使用以下API创建
Map.ofEntries(
entry(k1, v1),
entry(k2, v2),
entry(k3, v3),
// ...
entry(kn, vn));
-
改进的Stream API
Stream流模式是Java 8中引入的一个新特性,它允许你以声明式的方式处理数据集合。Stream API提供了一种高效、易于理解、易于链式调用的操作数据集合的方法。Java 9对stream API进行了加强,增加了4个新的操作方法。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
//1、takeWhile方法
List<Integer> takenNumbers = numbers.stream()
.takeWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println(takenNumbers); // 输出:[1, 2, 3]
//2、dropWhile方法
List<Integer> droppedNumbers = numbers.stream()
.dropWhile(n -> n < 4)
.collect(Collectors.toList());
System.out.println(droppedNumbers); // 输出:[4, 5, 6]
//3、重载的iterate方法,提供一个判断条件来指定什么时候结束循环
IntStream.iterate(1,i->i<20,i->i+1).forEach(System.out::println);
//4、ofNullable方法:允许创建一个空的stream
Stream.ofNullable(null);
-
进程API
java9中为了更好的控制和管理操作系统进程从而引入了改进的进程API,新的API主要涉及java.lang.ProcessHandle及其嵌套接口Info,这使得开发者可以很轻松获取本地进程的信息。
//1、获取当前进程
ProcessHandle current = ProcessHandle.current();
//2、获取全部进程
Stream<ProcessHandle> processHandleStream = ProcessHandle.allProcesses();
//3、根据进程id获取进程
ProcessHandle.of(current.pid());
-
响应式流(Reactive Streams)
Java9在Flow中引入响应式流API以支持响应式编程模型,其目的是实现以更加简单可靠的方式处理异步数据流。响应式编程适用于哪些基于事件驱动、非阻塞和高并发的应用程序。响应式流API有以下特征:
1.标准化接口:响应式流API中引入了一组标准化接口,包括有Publisher(发布者)、Subscriber(订阅者)、Subscription(订阅关系)及Processor(处理器);
2.背压支持:响应式流API引入了背压机制,它允许当数据流传输速度不匹配时订阅者通知发布者,让其减缓或者停止发送数据,该机制可以有效避免资源耗尽和应用程序崩溃情况;
3.标准实现:Java9在Flow中实现了响应流的标准化接口,包括有Flow.Publisher、Flow.Subscriber和Flow.Subscription。
//1、发布者
Publisher<String> publisher = new Publisher<>() {
@Override
public void subscribe(Subscriber<? super String> subscriber) {
subscriber.onSubscribe(new Subscription() {
@Override
public void request(long n) {
subscriber.onNext("hello");
subscriber.onNext("world!");
}
@Override
public void cancel() {
}
});
}
};
//2、订阅者
Subscriber<String> subscriber = new Subscriber<>() {
@Override
public void onSubscribe(Subscription subscription) {
subscription.request(2);
}
@Override
public void onNext(String item) {
System.out.println("next Elem is:" + item);
}
@Override
public void onError(Throwable throwable) {
}
@Override
public void onComplete() {
System.out.println("finished");
}
};
//3、订阅发布者
publisher.subscribe(subscriber);//输出:next Elem is:hello next Elem is:world!
-
HTTP/2客户端
Java9中定义了一个新的HTTP客户端API,该API实现HTTP/2和WebSocket,可以取代传统的HttpURLConnection API,该REFs将作为孵化器模块交付。
//1、创建客户端
HttpClient client = HttpClient.newHttpClient();
//2、创建request请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://openjdk.java.net/"))
.build();
//3、以异步的形式发送请求
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
-
G1设置为默认垃圾回收器
从Java9开始就将G1设置为默认的垃圾回收器,并移除了CMS垃圾回收器。G1是按照低时延的标准来设计的,旨在避免进行Full GC,Java9中G1的FullGC依然是以单线程加标记清除法来回收的。
2.1.2 JDK10新特性
-
局部变量类型推断
Java10中增强了Java语言,将类型推理扩展到具有初始化器的局部变量声明。这一特性意图是在保证Java对静态类型安全的前提下提升开发人员的体验。
var list=new ArrayList<String>();
System.out.println(list instanceof List);//输出true
var str="abc";
System.out.println(str);//输出abc
-
引入并行Full GC
JDK9中G1的Full GC是单线程去完成的,也就是说当需要Full GC的时候G1会严重影响性能,JDK10中对此做出改进,以多线程的方式并行回收垃圾,提高了Full GC效率。
2.1.3 JDK11新特性
-
Lambda参数的局部变量语法
Lambda是JDK8中引入的一个重要特性,JDK11中为了将隐式类型lambda表达式中的形式参数声明的语法与局部变量声明的语法对齐,从而引入Lambda参数的本地变量语法,示例如下:
List<String> sampleList = Arrays.asList("张三", "java 11","jack");
List<String> result = sampleList.stream()
// 过滤以j开头的字符串
.filter((@NotNull var s) -> s.startsWith("j"))
// 同时不包含11的字符串
.filter(Predicate.not((@NotNull var s) -> s.contains("11")))
.collect(Collectors.toList());
-
ZGC:一种可扩展的低延迟垃圾回收器(实验)
JDK 11引入了一个新的垃圾收集器,称为ZGC垃圾收集器。ZGC是一种低延迟的垃圾收集器,具有非常短的停顿时间,适用于需要快速响应和高吞吐量的应用程序。它可以在数毫秒的时间内处理几十GB的堆内存,适用于大内存的应用程序。
-
Epsilon:无操作垃圾回收器
Epsilon回收器实际上并不会进行垃圾回收,而是以牺牲内存占用和内存吞吐量为代价,提供一个具有有限分配限制和尽可能低的延迟开销的完全被动GC实现。一个成功的实现是一个独立的代码更改,不涉及其他GC,并且对JVM的其余部分进行最小的更改,一旦可用的Java堆用完,JVM就会关闭。
除此之外,JDK11中还推出了HTTP客户端的标准版,引入了一种新的类文件格式,支持在类文件中定义动态常,这使得Java程序可以更便捷地定义和使用常量值。
2.2 JDK12到JDK17新特性
2.2.1 JDK12新特性
-
Switch表达式改进(预览)
JDK12中对Switch表达式进行了扩展,使其既可以用作语句也可以用作表达式,并且这两种形式都可以使用“传统”或“简化”的作用域和控制流行为。这些变化将简化日常编码,这是JDK 12中的预览语言功能。
//改进前的Switch表达式
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}
//改进后的Switch表达式
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
//switch用于返回值
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
};
2.2.2 JDK13新特性
-
文本块(预览) JDK13中将文本块添加到Java语言中。文本块是一个多行字符串文字,它避免了大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。
//传统方式
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
Using a "two-dimensional" block of text
//文本块方式
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
2.2.3 JDK14新特性
-
instanceof的模式匹配(预览)
JDK14中通过instanceof运算符的模式匹配来增强Java编程语言。该特性允许程序中的通用逻辑,即从对象中有条件地提取组件,从而能够更简洁、更安全地表达。将类型判断和强制类型转换合二为一,从而手动显示进行强制类型转换。
Object obj=new String("hello world!")
//JDK14之前
if (obj instanceof String) {
String s = (String) obj;
// use s
}
//JDK14改进后
if (obj instanceof String s) {
// can use s here
} else {
// can't use s here
}
-
NullPointerExceptions
JDK14通过该新特性可以准确描述哪个变量为null,从而提高JVM生成的NullPointerException的可用性,更清楚地将动态异常和静态程序代码结合起来,提高研发效率,开启方式如下所示:
-XX:{+|-}ShowCodeDetailsInExceptionMessages
-
Record(预览)
record效果和Lombok的@Data类似,可以大幅减少数据类中的代码量
//record相当于编写了构造方法Student(name,age),getName(),getAge,重写了hashCode(),equal()和toString()
public record Student(String name,Integer age) {
}
在Java.lang.Class对象中添加Record对应的两个新方法:1)RecordComponent[] getRecordComponents();2) boolean isRecord()
2.2.4 JDK15新特性
-
Edwards曲线数字签名算法(EdDSA)
JDK15中根据RFC 8032规范描述,使用Edwards曲线数字签名算法(EdDSA)实现加密签名。EdDSA 是一种现代的椭圆曲线签名方案,其性能比现有的ECDSA实现(使用本机C代码)更好,同时具有相同的安全强度。
// 示例: 生成密钥对并签名
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair kp = kpg.generateKeyPair();
// algorithm is pure Ed25519
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(kp.getPrivate());
sig.update(msg);
byte[] s = sig.sign();
// 示例: 使用KeyFactory构造公钥
KeyFactory kf = KeyFactory.getInstance("EdDSA");
boolean xOdd = ...
BigInteger y = ...
NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519");
EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdPoint(xOdd, y));
PublicKey pubKey = kf.generatePublic(pubSpec);
-
密封类(预览,JDK17转正)
JDK15中使用密封类和接口增强Java编程语言。密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。例如在图形库中,只允许特定的类才能继承和扩展Shape类。通过密封类可以限制子类的集合从而简化模型。
//密封类之前的设计思路:1)使用final关键字使其没有子类;2)使类或其构造函数包称为私有的
abstract class AbstractStringBuilder {...}
public final class StringBuffer extends AbstractStringBuilder {...}
//密封类示例,Cat类和Dog类被允许继承密封(sealed)类Animal
public sealed class Animal permits Cat, Dog {
// 类的属性和方法
}
final class Cat extends Animal {
// Cat的实现
}
final class Dog extends Animal {
// Dog的实现
}
-
隐藏类
隐藏类是指那些不能被其他类的字节码直接使用的类,是为在运行时生成类并通过反射间接使用类的框架使用的。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类卸载。该提议将通过支持一个标准 API 来定义不可发现且生命周期有限的隐藏类,从而提高 JVM 上所有语言的效率。JDK 内部和外部的框架将能够动态生成类,而这些类可以定义隐藏类。隐藏类具有以下特点:
1)隐藏类天生为框架设计的,在运行时生成内部的 Class 2)隐藏类只能通过反射访问,不能直接被其他类的字节码访问 3)隐藏类可以独立于其他类加载、卸载,这可以减少框架的内存占用
1、隐藏类天生为框架设计的,在运行时生成内部的 Class 2、隐藏类只能通过反射访问,不能直接被其他类的字节码访问 3、隐藏类可以独立于其他类加载、卸载,这可以减少框架的内存占用
所以我们需要一些 API 来定义无法发现的且具有有限生命周期的隐藏类,如下所示:
java.lang.reflect.Proxy 可以定义隐藏类作为实现代理接口的代理类
java.lang.invoke.StringConcatFactory 可以生成隐藏类来保存常量连接方法
java.lang.invoke.LambdaMetaFactory 可以生成隐藏的 nestmate 类,以容纳访问封闭变量的 Lambda 主体
普通类是通过调用 ClassLoader::defineClass 创建的,而隐藏类是通过调用 Lookup::defineHiddenClass创建的。
2.2.5 JDK16新特性
-
矢量API(培养器)
为了在运行时可靠地编译为支持的CPU架构上的最佳矢量硬件指令的矢量计算,从而实现优于等效标量计算的性能。JDK16中提供孵化器模块的初始迭代,包含在jdk.innovar.vector中,该API应能够清晰简洁地表达由一系列矢量运算组成的广泛矢量计算,这些矢量运算通常在循环中组成,并可能具有控制流。
//使用矢量API之前的计算方式
void scalarComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
//使用矢量API计算
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;
void vectorComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i += SPECIES.length()) {
var m = SPECIES.indexInRange(i, a.length);
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i, m);
var vb = FloatVector.fromArray(SPECIES, b, i, m);
var vc = va.mul(va).
add(vb.mul(vb)).
neg();
vc.intoArray(c, i, m);
}
}
2.2.6 JDK17新特性
-
增强的伪随机数生成器
JDK17中为伪随机数生成器(PRNG)提供新的接口类型和实现,包括可跳变的PRNG和一类额外的可拆分PRNG算法(LXM),并且提供了一个新的接口RandomGenerator,它为所有现有和新的PRNG提供了统一的API。RandomGenerators提供名为int、longs、doubles、nextBoolean、nextInt、nextLong、nextDouble和nextFloot的方法及其所有当前参数变体。
//PRNG算法常见的实现:L32X64MixRandom、L32X64StarStarRandom、L64X128MixRandom、 L64X128StarStarRandom、L64X256MixRandom、L64X1024MixRandom、L128X128MixRandom、L128X256MixRandom、L128X1024MixRandom、Xoshiro256PlusPlus、Xoroshiro128PlusPlus
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L64X128StarStarRandom");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = 128X256MixRandom.create(System.currentTimeMillis());
// 生成随机数
System.out.println(randomGenerator.nextInt(1000));
-
外部函数和内存API(孵化)
为了解决非Java资源时仍然面临重大障碍:代码和数据与JVM在同一台机器上,但在Java运行时之外。JDK17中通过引入API,Java程序可以通过该API与Java运行时之外的代码和数据进行互操作。通过有效地调用外部函数(即JVM外部的代码),并通过安全地访问外部内存(即不受JVM管理的内存),API使Java程序能够调用本机库并处理本机数据,而不会出现JNI的脆弱性和危险性。
2.3 JDK18到JDK21新特性
2.3.1 JDK18新特性
-
将UTF-8设置为默认字符
JDK18中指定UTF-8作为标准Java API的默认字符集。通过此更改,依赖于默认字符集的API将在所有实现、操作系统、区域设置和配置中保持一致。
-
优化Java API文档中的代码片段
//JDK18之前
<pre>{@code
lines of source code
}</pre>
//JDK18之后,通过@snippet注解
/**
* The following code shows how to use {@code Optional.isPresent}:
* {@snippet :
* if (v.isPresent()) {
* System.out.println("v: " + v.get());
* }
* }
*/
-
互联网地址解析
JDK18中定义一个用于主机名和地址解析的服务提供程序接口(SPI),以便java.net。InetAddress可以使用平台内置解析器以外的解析器。
var addressBytes = new byte[] { (byte) 10, (byte) 251,74,92};
var resolvedHostName = InetAddress.getByAddress(addressBytes)
.getCanonicalHostName();
System.out.println(resolvedHostName); //test2.dhds.cn
2.3.2 JDK19新特性
-
结构化并发API(孵化)
JDK19中通过引入用于结构化并发的API来简化多线程编程,并不是为了取代java.util.concurrent
。结构化并发将在不同线程中运行的多个任务视为一个工作单元,从而简化错误处理和关闭,提高可靠性,并增强可观察性。
结构化并发API的主要类是StructuredTaskScope。此类允许开发人员将任务构造为一系列并发子任务,并将它们作为一个单元进行协调。子任务是在它们自己的线程中执行的,方法是将它们单独分叉,然后将它们连接为一个单元,并可能将它们作为一个单元取消。子任务的成功结果或异常由父任务聚合和处理。StructuredTaskScope将子任务或fork的生存期限制在一个清晰的词法范围内,任务与其子任务的所有交互——forking、join、cancelling、handling error和compositing results——都发生在该范围内。结构化并发非常适合虚拟线程,虚拟线程是 JDK 实现的轻量级线程。
//非结构化并发API时的子任务执行,子任务失败会抛异常,产生很多问题
Response handle() throws ExecutionException, InterruptedException {
Future<String> user = esvc.submit(() -> findUser());
Future<Integer> order = esvc.submit(() -> fetchOrder());
String theUser = user.get(); // Join findUser
int theOrder = order.get(); // Join fetchOrder
return new Response(theUser, theOrder);
}
//结构化并发
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> findUser());
Future<Integer> order = scope.fork(() -> fetchOrder());
scope.join(); // 加入子任务
scope.throwIfFailed(); // ... and propagate errors
// 到这一步所有的子任务都执行成功,返回结构
return new Response(user.resultNow(), order.resultNow());
}
}
ShutdownOnFailure:一个子任务失败(即调用所有子任务)
ShutdownOnSuccess:其中一个成功(即调用任何子任务)则取消所有子任务
StructuredTaskScope:同时运行一组任务,如果其中任何一个失败,它就会失败
-
虚拟线程
Java程序在运行时线程和操作系统线程不是一一对应的,可以将大量虚拟线程映射到少量操作系统线程,借助这一技术,JDK19中将虚拟线程引入Java平台,虚拟线程是轻量级线程,可以显著减少编写和维护高吞吐量并发应用程序的工作量。虚拟线程能提高应用程序处理请求的吞吐量。
图3:虚拟线程、平台线程和系统内核线程的关系
虚拟线程技术运行代码的速度并不比平台线程快,它们的优势是提供规模(即更高的吞吐量)而不是速度。
//虚拟线程示例
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
System.out.println(i);
return i;
});
});
}
2.3.3 JDK21新特性
-
字符串模板(预览)
JDK21中使用字符串模板来增强Java编程语言。字符串模板通过将文本与嵌入的表达式和模板处理器耦合来产生专门的结果,从而补充Java现有的字符串文本和文本块。这样做有以下好处:
1)通过使包含运行时计算的值的字符串易于表达,简化了Java程序的编写;
2)增强混合文本和表达式的表达式的可读性,无论文本是在单个源行上(如字符串文字)还是跨越多个源行(如文本块);
3)允许Java库定义字符串模板中使用的格式化语法来保持灵活性;
4)允许创建根据文字文本和嵌入表达式计算的非字符串值,而不必通过中间字符串表示;
5)简化接受用非Java语言(如SQL、XML和JSON)编写的字符串的API的使用。
String firstName = "Bill";
String lastName = "Duck";
String fullName = STR."\{firstName} \{lastName}";// "Bill Duck"
-
序列化集合
JDK21中引入新的接口来表示具有定义顺序的集合。每个这样的集合具有定义明确的第一元素、第二元素等等,直到最后一个元素。它还提供了统一的API,用于访问和处理它的第一个和最后一个元素,并支持从第一个到最后一个和从最后一个到第一个(即正向和反向)处理元素。有序集合包含三个接口,分别为SequencedCollection、SequencedSet和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();
}
//SequencedSet接口
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> {
SequencedSet<E> reversed(); // covariant override
}
//SequencedMap接口
interface SequencedMap<K,V> extends Map<K,V> {
// new methods
SequencedMap<K,V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K,V>> sequencedEntrySet();
V putFirst(K, V);
V putLast(K, V);
// methods promoted from NavigableMap
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
}
图4 集合类型层次结构
-
switch的模式匹配
JDK21中通过switch表达式和语句的模式匹配来增强Java编程语言。通过将模式匹配扩展到switch,可以针对多个模式测试表达式,每个模式都有一个特定的操作,从而可以简洁、安全地表达复杂的面向数据的查询。
//JDK21之前的方式
static void testFooBarOld(String s) {
if (s == null) {
System.out.println("Oops!");
return;
}
switch (s) {
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
//JDK21中的方式
static void testFooBarNew(String s) {
switch (s) {
case null -> System.out.println("Oops");
case "Foo", "Bar" -> System.out.println("Great");
default -> System.out.println("Ok");
}
}
//类型模式匹配
static void patternSwitchTest(Object obj) {
String formatted = switch (obj) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %f", d);
case String s -> String.format("String %s", s);
default -> obj.toString();
};
}
参考文献
[1] The Arrival of Java 21 The Arrival of Java 21
[2] Open JDK OpenJDK
[3] OpenJDK 初步探索 OpenJDK 初步探索-优快云博客
[4] new relic. 2023 State of the Java Ecosystem