一起来学习下 Java 20 的新功能
Amber 项目
switch 模式匹配 Record 模式
Amber 项目的目标是探索和孵化更小、以生产力为导向的 Java 语言功能。
static String apply(Effect effect) { String formatted = ""; if (effect instanceof Delay de) { formatted = String.format("Delay active of %d ms.", de.timeInMs()); } else if (effect instanceof Reverb re) { formatted = String.format("Reverb active of type %s and roomSize %d.", re.name(), re.roomSize()); } else if (effect instanceof Overdrive ov) { formatted = String.format("Overdrive active with gain %d.", ov.gain()); } else if (effect instanceof Tremolo tr) { formatted = String.format("Tremolo active with depth %d and rate %d.", tr.depth(), tr.rate()); } else if (effect instanceof Tuner tu) { formatted = String.format("Tuner active with pitch %d. Muting all signal!", tu.pitchInHz()); } else { formatted = String.format("Unknown effect active: %s.", effect); } return formatted;}static String apply(Effect effect) { return switch(effect) { case Delay de -> String.format("Delay active of %d ms.", de.timeInMs()); case Reverb re -> String.format("Reverb active of type %s and roomSize %d.", re.name(), re.roomSize()); case Overdrive ov -> String.format("Overdrive active with gain %d.", ov.gain()); case Tremolo tr -> String.format("Tremolo active with depth %d and rate %d.", tr.depth(), tr.rate()); case Tuner tu -> String.format("Tuner active with pitch %d. Muting all signal!", tu.pitchInHz()); case null, default -> String.format("Unknown or empty effect active: %s.", effect); };}static String apply(Effect effect, Guitar guitar) { return switch(effect) { case Delay de -> String.format("Delay active of %d ms.", de.timeInMs()); case Reverb re -> String.format("Reverb active of type %s and roomSize %d.", re.name(), re.roomSize()); case Overdrive ov -> String.format("Overdrive active with gain %d.", ov.gain()); case Tremolo tr -> String.format("Tremolo active with depth %d and rate %d.", tr.depth(), tr.rate()); case Tuner tu when !guitar.isInTune() -> String.format("Tuner active with pitch %d. Muting all signal!", tu.pitchInHz()); case Tuner tu -> "Guitar is already in tune."; case null, default -> String.format("Unknown or empty effect active: %s.", effect); };}这里的 guard 确保复杂的布尔逻辑仍然可以用简洁的方式表示。在 case 分支中嵌套 if 语句来测试这种逻辑不仅更冗长,而且还可能引入我们一开始想避免的小错误。
Java 19 和 Java 20 相比有什么不同
与 Java 19 相比,这个功能做了一些改变:
当一个模式匹配突然失败时,现在会抛出一个 MatchException 在 switch 表达式和语句中支持推断 record 模式的类型参数。这意味着现在可以在要匹配的模式中使用 var
JEP 432:Record 模式(第二预览版)
static String apply(Effect effect) { return switch(effect) { case Delay(int timeInMs) -> String.format("Delay active of %d ms.", timeInMs); case Reverb(String name, int roomSize) -> String.format("Reverb active of type %s and roomSize %d.", name, roomSize); case Overdrive(int gain) -> String.format("Overdrive active with gain %d.", gain); case Tremolo(int depth, int rate) -> String.format("Tremolo active with depth %d and rate %d.", depth, rate); case Tuner(int pitchInHz) -> String.format("Tuner active with pitch %d. Muting all signal!", pitchInHz); case null, default -> String.format("Unknown or empty effect active: %s.", effect); };}record Tuner(int pitchInHz, Note note) implements Effect {}record Note(String note) {}class TunerApplier { static String apply(Effect effect, Guitar guitar) { return switch(effect) { case Tuner(int pitch, Note(String note)) -> String.format("Tuner active with pitch %d on note %s", pitch, note); }; }}class TunerApplier { static String apply(Effect effect, Guitar guitar) { return switch(effect) { case Tuner(var pitch, Note(var note)) -> String.format("Tuner active with pitch %d on note %s", pitch, note); }; }}这里对于嵌套模式 Tuner(var pitch, Note(var note)) 的类型参数进行了推断。目前仅支持嵌套模式的隐式类型推断;类型模式尚不支持隐式类型参数的推断。因此,类型模式 Tuner tu 总是被视为原始类型模式。
增强的 for 语句
记录模式现在也被允许用于增强的 for 语句中,这使得循环遍历记录值的集合并快速提取每个记录的组件变得容易:
record Delay(int timeInMs) implements Effect {}
class DelayPrinter { static void printDelays(List<Delay> delays) { for (Delay(var timeInMs) : delays) { System.out.println("Delay found with timeInMs=" + timeInMs); } }}Java 19 与 Java 20 的不同之处
添加对泛型记录模式类型参数推断的支持; 添加对记录模式出现在增强for语句标题中的支持。
虚拟线程 有作用域的值 结构化并发
Loom 项目旨在通过引入虚拟线程和结构化并发 API 等方式来简化 Java 中并发应用程序的维护。
JEP 436:虚拟线程(第二个预览版)
典型用例
创建虚拟线程
Response handle() throws IOException { String theUser = findUser(); int theOrder = 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(); // Join both forks scope.throwIfFailed(); // ... and propagate errors // Here, both forks have succeeded, so compose their results return new Response(user.resultNow(), order.resultNow()); }}注意:Java 19 中的 ExecutorService 接口进行了调整,扩展了 AutoCloseable。因此,现在可以在 try-with-resources 结构中使用它。
与 Java 19 有何不同
此功能处于“第二个预览版”阶段,以便获得更多反馈。除此之外,还有一些API更改已经成为常规功能,不再提供预览。这是因为它们涉及的功能通常很有用,不仅适用于虚拟线程,还包括:
Thread 类中的新方法: join(Duration); sleep(Duration); threadId(). Future 中的新方法(用于检查任务状态和结果) ExecutorService 扩展 AutoCloseable,以便可以在 try-with-resources 块中使用
JEP 429: 作用域值 Scoped Value(孵化器)
ThreadLocal
作用域值
final static ScopedValue < ... > V = ScopedValue.newInstance();// In some methodScopedValue.where(V, < value > ) .run(() - > { ...V.get()...call methods... });// In a method called directly or indirectly from the lambda expression...V.get()...典型应用场景
与 Java 19 有什么不同
JEP 437:结构化并发(第二个孵化器版本)
Future<String> user = executor.submit(() -> findUser());
Future<Integer> order = executor.submit(() -> fetchOrder());
String theUser = user.get(); // Join findUser
int theOrder = order.get(); // Join fetchOrder
return new Response(theUser, theOrder);
}
Response handle() throws ExecutionException, InterruptedException { Future < String > user = executor.submit(() - > findUser()); Future < Integer > order = executor.submit(() - > fetchOrder()); String theUser = user.get(); // Join findUser int theOrder = order.get(); // Join fetchOrder return new Response(theUser, theOrder);}Response handle() throws IOException { String theUser = findUser(); int theOrder = 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(); // Join both forks scope.throwIfFailed(); // ... and propagate errors // Here, both forks have succeeded, so compose their results return new Response(user.resultNow(), order.resultNow()); }}在结构化并发中,子任务代表任务工作。任务等待子任务的结果并监视它们的失败。StructuredTaskScope 类允许开发人员将任务结构化为并发子任务家族,并将它们作为一个单元协调。子任务通过单独分叉它们在自己的线程中执行,然后将它们作为一个单元加入并可能作为一个单元取消。父任务处理子任务的成功结果或异常。
与原始示例相比,这里涉及的线程的生命周期理解起来很容易。在所有情况下,它们的生命周期都限于词法作用域,即 try-with-resources 语句的主体。此外,使用 StructuredTaskScope 确保了许多有价值的属性:
短路错误处理:如果子任务失败,则另一个子任务将被取消(如果它尚未完成)。这由 ShutdownOnFailure 实现的取消策略管理;其他策略,如 ShutdownOnSuccess 也是可用的。 取消传播:如果在调用 join() 之前或期间中断运行 handle() 的线程,则当该线程退出作用域时,两个分叉将自动取消。 清晰可见:上面的代码具有明确的结构:设置子任务,等待它们完成或被取消,然后决定是成功(并处理子任务的结果,这些结果已经完成)还是失败(子任务已经完成,因此没有更多的清理工作)。
与 Java 19 有何不同
Panama 项目
外部函数与内存 API Vector API
JEP 434:外部函数 & 内存 API(第二个预览版)
示例代码
// 1. Find foreign function on the C library path
Linker linker = Linker.nativeLinker();SymbolLookup stdlib = linker.defaultLookup();MethodHandle radixsort = linker.downcallHandle(stdlib.find("radixsort"), ...);// 2. Allocate on-heap memory to store four stringsString[] javaStrings = { "mouse", "cat", "dog", "car" };// 3. Use try-with-resources to manage the lifetime of off-heap memorytry (Arena offHeap = Arena.openConfined()) { // 4. Allocate a region of off-heap memory to store four pointers MemorySegment pointers = offHeap.allocateArray(ValueLayout.ADDRESS, javaStrings.length); // 5. Copy the strings from on-heap to off-heap for (int i = 0; i < javaStrings.length; i++) { MemorySegment cString = offHeap.allocateUtf8String(javaStrings[i]); pointers.setAtIndex(ValueLayout.ADDRESS, i, cString); } // 6. Sort the off-heap data by calling the foreign function radixsort.invoke(pointers, javaStrings.length, MemorySegment.NULL, '\0'); // 7. Copy the (reordered) strings from off-heap to on-heap for (int i = 0; i < javaStrings.length; i++) { MemorySegment cString = pointers.getAtIndex(ValueLayout.ADDRESS, i); javaStrings[i] = cString.getUtf8String(0); }} // 8. All off-heap memory is deallocated hereassert Arrays.equals(javaStrings, new String[] {"car", "cat", "dog", "mouse"}); // true让我们仔细看看代码中用到的一些类型,了解它们在外部函数和内存 API 中的功能和目的:
Linker: 提供了从Java代码访问外部函数和从外部函数访问Java代码的功能。它通过downcall方法句柄允许Java代码链接到外部函数。它还通过生成upcall stubs允许外部函数调用Java方法句柄。有关更多信息,请参见此类型的JavaDoc。 SymbolLookup: 检索一个或多个库中符号的地址。有关更多信息,请参见此类型的JavaDoc。 Arena: 控制内存段的生命周期。Arena具有称为竞技场范围的范围。当竞技场关闭时,竞技场范围不再存在。因此,与竞技场范围关联的所有段都无效,它们的支撑内存区域被释放(在适用的情况下),并且在竞技场关闭后不能再访问它们。有关更多信息,请参见此类型的JavaDoc。 MemorySegment: 提供对连续内存区域的访问。有两种类型的内存段:heap segments(在Java内存堆中)和native segments(在Java内存堆之外)。有关更多信息,请参见此类型的JavaDoc。 ValueLayout: 对基本数据类型的值进行建模,例如integral值、floating-point值和address值。除此之外,它还为Java原始类型和地址定义了有用的值布局常量。有关更多信息,请参见此类型的JavaDoc。
与 Java 19 有何不同
合并了 MemorySegment 和 MemoryAddress 抽象(内存地址现在由零长度的内存段建模); 密封的 MemoryLayout 层次结构得到了增强,以便与 switch 模式匹配一起使用; MemorySession 已被分成 Arena 和 SegmentScope,以便跨维护边界共享段。
JEP 438 Vector 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; }}
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) { int i = 0; int upperBound = SPECIES.loopBound(a.length); for (; i < upperBound; i += SPECIES.length()) { // FloatVector va, vb, vc; var va = FloatVector.fromArray(SPECIES, a, i); var vb = FloatVector.fromArray(SPECIES, b, i); var vc = va.mul(va) .add(vb.mul(vb)) .neg(); vc.intoArray(c, i); } for (; i < a.length; i++) { c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f; }}从 Java 开发者的角度来看,这只是表达标量计算的另一种方式。它可能会显得略微冗长,但另一方面它可以带来惊人的性能提升。
典型用例
Vector API 提供了一种在 Java 中编写复杂向量算法的方法,例如向量化的 hashCode 实现或专门的数组比较。许多领域可以从中受益,包括机器学习、线性代数、加密、文本处理、金融和 JDK 本身的代码。
与 Java 19 有何不同
除了一小部分错误修复和性能增强外,这个特性与 Valhalla 项目的对齐是与 Java 19 最大的不同之处。它的意义是非常明确的,因为向量 API 和 Valhalla 项目都专注于性能提升。
回想一下,Valhalla 项目的目标是通过值对象和用户自定义原语增强 Java 对象模型,将面向对象编程的抽象与简单原语的性能特性相结合。
一旦 Valhalla 项目的功能可用,将会适应 Vector API 以利用值对象,届时它将被提升为预览功能。
转自:Hanno Embregts,
链接:foojay.io/today/its-java-20-release-day-heres-whats-new/
END
往期精彩Spring Boot + MDC 实现全链路调用日志跟踪
一种非侵入式幂等性的Java实现
Logback 自定义日志脱敏组件
Spring Boot + OpenAI 生成图像