Contents

Java 17中的新功能

1. 概述

在本教程中,我们将讨论与 Java 生态系统新版本Java SE 17 相关的新闻,包括新功能及其发布过程中的变化、LTS 支持和许可证。

2. JEP列表

首先,让我们谈谈什么会影响 Java 开发人员的日常工作。

2.1. 恢复始终严格的浮点语义(JEP 306

该 JEP 主要用于科学应用,它使浮点运算始终保持严格。默认浮点运算是strictstrictfp,两者都保证每个平台上的浮点计算结果相同。 在 Java 1.2 之前,strictfp行为也是默认行为。然而,由于硬件问题,架构师发生了变化,关键字strictfp是重新启用这种行为所必需的。因此,不再需要使用此关键字。

2.2. 增强的伪随机数生成器 ( JEP 356 )

还与更特殊的用例相关,JEP 356 为伪随机数生成器 (PRNG) 提供了新的接口和实现。

因此,可以更轻松地互换使用不同的算法,并且它还为基于流的编程提供了更好的支持:

public IntStream getPseudoInts(String algorithm, int streamSize) {
    // returns an IntStream with size @streamSize of random numbers generated using the @algorithm
    // where the lower bound is 0 and the upper is 100 (exclusive)
    return RandomGeneratorFactory.of(algorithm)
            .create()
            .ints(streamSize, 0,100);
}

传统的随机类,例如java.util.RandomSplittableRandom 和SecureRandom 现在扩展了新的RandomGenerator接口。

2.3. 新的 macOS 渲染管道 ( JEP 382 )

自 Apple 弃用在 Swing GUI 内部使用的 OpenGL API(在 macOS 10.14 中)以来,此 JEP 为 macOS 实现了 Java 2D 内部渲染管道。新实现使用 Apple Metal API,除了内部引擎之外,现有 API 没有任何变化。

2.4. macOS/AArch64端口(JEP 391

Apple 宣布了一项将其计算机产品线从 X64 过渡到 AArch64 的长期计划。此 JEP 将 JDK 移植到 macOS 平台上的 AArch64 上运行。

2.5. 弃用用于删除的 Applet API ( JEP 398 )

虽然这对于许多使用 Applet API 开始其开发生涯的 Java 开发人员来说可能会感到难过,但许多 Web 浏览器已经取消了对 Java 插件的支持。随着 API 变得无关紧要,此版本将其标记为删除,即使它自版本 9 以来已被标记为已弃用。

2.6. 强烈封装 JDK 内部(JEP 403

JEP 403 代表了朝着强烈封装 JDK 内部迈出的又一步,因为它删除了标志*–illegal-access*。平台将忽略该标志,如果存在该标志,控制台将发出一条消息,通知该标志的终止。 此功能将阻止 JDK 用户访问内部 API,但诸如sun.misc.Unsafe之类的关键 API 除外。

2.7. 开关模式匹配(预览版)(JEP 406

这是通过增强switch表达式和语句的模式匹配向模式匹配迈出的又一步。它减少了定义这些表达式所需的样板文件,并提高了语言的表现力。 让我们看一下新功能的两个示例:

static record Human (String name, int age, String profession) {}
public String checkObject(Object obj) {
    return switch (obj) {
        case Human h -> "Name: %s, age: %s and profession: %s".formatted(h.name(), h.age(), h.profession());
        case Circle c -> "This is a circle";
        case Shape s -> "It is just a shape";
        case null -> "It is null";
        default -> "It is an object";
    };
}
public String checkShape(Shape shape) {
    return switch (shape) {
        case Triangle t && (t.getNumberOfSides() != 3) -> "This is a weird triangle";
        case Circle c && (c.getNumberOfSides() != 0) -> "This is a weird circle";
        default -> "Just a normal shape";
    };
}

2.8. 删除 RMI 激活 ( JEP 407 )

在版本 15 中标记为删除,此 JEP 在版本 17 中从平台中删除了 RMI 激活 API。

2.9. 密封类(JEP 409

密封类是Project Amber 的一部分,这个 JEP 正式为该语言引入了一项新功能,尽管它在 JDK 版本1516 中以预览模式提供。 该功能限制了哪些其他类或接口可以扩展或实现密封组件。展示与模式匹配相关的另一项改进,结合 JEP 406 将允许对类型、强制转换和行为代码模式进行更复杂和更清晰的检查。 让我们看看它的实际效果:

int getNumberOfSides(Shape shape) {
    return switch (shape) {
        case WeirdTriangle t -> t.getNumberOfSides();
        case Circle c -> c.getNumberOfSides();
        case Triangle t -> t.getNumberOfSides();
        case Rectangle r -> r.getNumberOfSides();
        case Square s -> s.getNumberOfSides();
    };
}

2.10.删除实验性 AOT 和 JIT 编译器 ( JEP 410 )

GraalVM ( JEP-317 ) 中的 Ahead-Of-Time (AOT) 编译 ( JEP 295 ) 和 Just-In-Time (JIT) 编译器分别作为实验性特性引入 JDK 9 和 JDK 10维护费用。 另一方面,他们没有大量采用。因此,这个 JEP 将它们从平台中移除,但开发人员仍然可以使用GraalVM 来利用它们。

2.11. 弃用安全管理器以进行删除 ( JEP 411 )

旨在保护客户端 Java 代码的安全管理器是另一个标记为删除的功能,因为不再相关。

2.12. 外部函数和内存 API(孵化器)(JEP 412

Foreign Function and Memory API 允许 Java 开发人员从 JVM 外部访问代码并管理堆外的内存。目标是替换JNI API 并与旧 API 相比提高安全性和性能。 此 API 是Project Panama 开发的另一个功能,它已由 JEP 393、389、383 370演变  和淘汰

使用此功能,我们可以从 Java 类调用 C 库:

private static final SymbolLookup libLookup;
static {
    // loads a particular C library
    var path = JEP412.class.getResource("/print_name.so").getPath();
    System.load(path);
    libLookup = SymbolLookup.loaderLookup();
}

首先,有必要通过 API 加载我们希望调用的目标库。 接下来,我们需要指定目标方法的签名并最终调用它:

public String getPrintNameFormat(String name) {
    var printMethod = libLookup.lookup("printName");
    if (printMethod.isPresent()) {
        var methodReference = CLinker.getInstance()
            .downcallHandle(
                printMethod.get(),
                MethodType.methodType(MemoryAddress.class, MemoryAddress.class),
                FunctionDescriptor.of(CLinker.C_POINTER, CLinker.C_POINTER)
            );
        try {
            var nativeString = CLinker.toCString(name, newImplicitScope());
            var invokeReturn = methodReference.invoke(nativeString.address());
            var memoryAddress = (MemoryAddress) invokeReturn;
            return CLinker.toJavaString(memoryAddress);
        } catch (Throwable throwable) {
            throw new RuntimeException(throwable);
        }
    }
    throw new RuntimeException("printName function not found.");
}

2.13. Vector API(第二个孵化器)(JEP 414

Vector API 处理 SIMD(单指令多数据)类型的操作,这意味着并行执行的各种指令集。它利用支持向量指令的专用 CPU 硬件,并允许执行流水线等指令。 因此,新的 API 将使开发人员能够实现更高效的代码,充分利用底层硬件的潜力。 此操作的日常用例是科学代数线性应用程序、图像处理、字符处理,以及任何繁重的算术应用程序或任何需要对多个独立操作数应用操作的应用程序。 让我们使用 API 来说明一个简单的向量乘法示例:

public void newVectorComputation(float[] a, float[] b, float[] c) {
    for (var i = 0; i < a.length; i += SPECIES.length()) {
        var m = SPECIES.indexInRange(i, a.length);
        var va = FloatVector.fromArray(SPECIES, a, i, m);
        var vb = FloatVector.fromArray(SPECIES, b, i, m);
        var vc = va.mul(vb);
        vc.intoArray(c, i, m);
    }
}
public void commonVectorComputation(float[] a, float[] b, float[] c) {
    for (var i = 0; i < a.length; i ++) {
        c[i] = a[i] * b[i];
    }
}

2.14. 特定于上下文的反序列化过滤器 ( JEP 415 )

JEP 290 首次在 JDK 9 中引入,使我们能够验证来自不受信任来源的传入序列化数据,这是许多安全问题的常见来源。该验证发生在 JVM 级别,从而提供更高的安全性和稳健性。

使用 JEP 415,应用程序可以配置在 JVM 级别定义的特定于上下文和动态选择的反序列化过滤器。每个反序列化操作都会调用这样的过滤器。

3. LTS 定义

这些变化不仅仅停留在代码中——流程也在发生变化。 Java 平台发布的历史悠久且不精确,这是众所周知的。尽管设计为在发布之间有三年的节奏,但它通常变成了一个四年的过程。 此外,鉴于市场的新动态,创新和快速响应变得势在必行,负责平台演进的团队决定改变发布节奏以适应新的现实。 因此,自 Java 10(2018 年 3 月 20 日发布)以来,采用了新的 6 个月功能发布模型。

3.1. 六个月的功能发布模型

**新的六个月功能发布模型允许平台开发人员在准备好时发布功能。**这消除了将功能推入版本的压力。否则,他们将不得不等待三到四年才能将该功能提供给平台的用户。 新模型还改善了用户和平台架构师之间的反馈周期。这是因为功能可以在孵化模式下提供,并且只有在多次交互后才发布以供一般使用。

3.2. LTS 模型

由于企业应用程序广泛使用 Java,因此稳定性至关重要。此外,继续为所有这些版本提供支持和补丁更新的成本很高。

因此,创建了长期支持 (LTS) 版本,为用户提供扩展支持。因此,由于错误修复、性能改进和安全补丁,此类版本自然会变得更加稳定和安全。对于 Oracle,这种支持通常持续八年。 由于引入了发布模型的变化,LTS 版本为 Java SE 11(2018 年 9 月发布)和 Java SE 17(2021 年 9 月发布)。尽管如此,版本 17 为模型带来了一些新的东西。简而言之,LTS 版本之间的间隔现在是两年而不是三年,这使得 Java 21(计划于 2023 年 9 月发布)可能是下一个 LTS。 还有一点值得一提的是,这个发布模式并不新鲜。它被无耻地复制并改编自其他项目,例如 Mozilla Firefox、Ubuntu 和其他模型证明了自己的项目。

4. 新发布流程

我们基于JEP 3 撰写本文,因为它描述了流程中的所有更改。请检查它以获取更多详细信息。我们将尝试在这里提供一个简明的摘要。 鉴于上述新模型,再加上平台的不断发展和新的六个月发布节奏(通常是六月和十二月),Java 将发展得更快。JDK 的开发团队将按照下面描述的过程启动下一个功能版本的发布周期。 该过程从main-line 的分叉开始。然后在稳定存储库 JDK/JDK$N(例如JDK17 )中继续开发。在那里,开发继续关注发布的稳定性。 在我们深入研究这个过程之前,让我们澄清一些术语:

  • 错误:在这种情况下,错误意味着票证或任务:
    • 当前:这些是与当前版本相关的实际错误(即将发布的新版本)或对已包含在此版本中的新功能的调整(新 JEP)。
    • 目标:与旧版本相关,并计划在此新版本中修复或解决
  • 优先级:从 P1 到 P5,其中 P1 最重要,重要性逐渐降低到 P5

4.1. 新格式

稳定过程将在接下来的三个月进行:

  • JDK/JDK$N 存储库就像一个发布分支,此时,没有新 JEP 的新 JEP 进入存储库。
  • 接下来,该存储库中的开发将稳定并移植到其他开发继续进行的主线。
  • 减速阶段 1 (RDP 1):持续四到五周。开发人员放弃所有当前 P4-P5 和目标 P1-P3(取决于延迟修复  或增强 )。这意味着 P5+ 测试/文档错误和目标 P3+ 代码错误是可选的。
  • 减速阶段 2 (RDP 2):持续三到四个星期。现在他们推迟所有当前 P3-P5 和目标 P1-P3(取决于deferringfix  或enhancement )。
  • 最后,该团队发布了一个发布候选版本并将其提供给公众。此阶段持续两到五周,仅解决当前的 P1 修复(使用fix )。

一旦所有这些周期完成,新版本将成为通用 (GA) 版本。

5. 下一步是什么?

JDK 架构师继续致力于许多旨在使平台现代化的项目。目标是提供更好的开发体验以及更健壮和高性能的 API。 因此,JDK 18 应该会在六个月后发布,尽管这个版本不太可能包含重大或破坏性的变化。我们可以在官方OpenJDK 项目 门户中关注针对此版本的建议 JEP 列表。 另一个影响当前和未来版本的相关新闻是适用于 Oracle JDK 发行版(或 Hotspot)的新的免费条款和条件 许可。在大多数情况下,Oracle 为生产和其他环境免费提供其分发,但也有一些例外。再次,请参阅链接。 如前所述,新流程的目标是下一个 LTS 版本为 21 版,并计划在 2023 年 9 月之前发布。