自学内容网 自学内容网

Java新特性梳理——Java17


highlight: xcode

theme: vuepress

概述

JDK 16 刚发布半年(2021/03/16),JDK 17 又如期而至(2021/09/14),这个时间点特殊,蹭苹果发布会的热度?记得当年 JDK 15 的发布也是同天。

Oracle 宣布,从 JDK 17 开始,后面的 JDK 都全部免费提供!!!

image.png

image.png

JDK 17 是自 2018 年 JDK 11 后的第二个长期支持版本,支持到 2029 年 9 月,支持时间长达 8 年,这下可以不用死守 JDK 8 了,JDK 17+ 也可以是一种新的选择了。下一个第三个长期支持版本是 JDK 21,时间为 2023 年 9 月,这次长期支持版本发布计划改了,不再是原来的 3 年一次,而是改成了 2 年一次!非长期支持版本还是半年发一次不变,下一个非长期支持版本计划在 2022/03 发布。

语法层面变化

密封类

密封类,这个特性在 JDK 15 中首次成为预览特性,在 JDK 16 中进行二次预览,在 JDK 17 这个版本中终于正式转正了。

密封类是由 JEP 360 提出的,并在 JDK 15 中作为预览功能提供。它们由 JEP 397 再次提出并进行了改进,并作为预览功能在 JDK 16 中提供。该 JEP 建议在 JDK 17 中完成密封类,与 JDK 16 没有任何变化。

switch模式匹配(预览)

使用 switch 表达式和语句的模式匹配以及对模式语言的扩展来增强 Java 编程语言。扩展模式匹配以 switch允许针对多个模式测试表达式,每个模式都有特定的操作,以便可以简洁安全地表达复杂的面向数据的查询。

instanceof 模式匹配是 Java 14 非常赞的一个新特性!这次在 JDK 17 中为 switch 语句支持模式匹配。

目标:

  • switch 通过允许模式出现在 case 标签中来扩展表达式和语句的表现力和适用性。
  • 允许在 switch 需要时放松对历史的零敌意。
  • 引入两种新的模式:保护模式,允许使用任意布尔表达式细化模式匹配逻辑,以及带括号的模式,以解决一些解析歧义。
  • 确保所有现有的 switch表达式和语句继续编译而不做任何更改并以相同的语义执行。
  • 不要引入 switch 与传统 switch 构造分离的具有模式匹配语义的新式表达式或语句。
  • switchcase 标签是模式与 case 标签是传统常量时,不要使表达式或语句的行为不同。

旧的写法:

java static String formatter(Object o) { String formatted = "unknown"; if (o instanceof Integer i) { formatted = String.format("int %d", i); } else if (o instanceof Long l) { formatted = String.format("long %d", l); } else if (o instanceof Double d) { formatted = String.format("double %f", d); } else if (o instanceof String s) { formatted = String.format("String %s", s); } return formatted; }

支持模式匹配的 switch:

java static String formatterPatternSwitch(Object o) { return switch (o) { 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 -> o.toString(); }; }

直接在 switch 上支持 Object 类型,这就等于同时支持多种类型,使用模式匹配得到具体类型,大大简化了语法量,这个功能还是挺实用的, 目前看转正只是一个时间上的问题而已。

API层面变化

Vector API(二次孵化)

引入一个 API 来表达向量计算,这些计算在运行时可靠地编译为支持的 CPU 架构上的最佳向量指令,从而实现优于等效标量计算的性能。

Vector API 由 JEP 338 提出并作为孵化 API 集成到 Java 16 中。我们在此建议结合改进以响应反馈以及性能改进和其他重要的实施改进。我们包括以下显着变化:

  • 增强 API 以支持字符操作,例如 UTF-8 字符解码。具体来说,我们添加了在 short 向量和 char 数组之间复制字符的方法,以及用于与整数向量进行无符号比较的新向量比较运算符。
  • 用于将 byte 向量与 boolean 数组相互转换的 API 的增强功能。
  • 使用英特尔的短向量数学库(SVML)对 x64 上的超越和三角泳道运算的内在支持。
  • Intel x64 和 ARM NEON 实现的一般性能增强。

特定于上下文的反序列化过滤器

允许应用程序通过 JVM 范围的过滤器工厂配置特定于上下文和动态选择的反序列化过滤器,该工厂被调用以为每个单独的反序列化操作选择一个过滤器。

反序列化不受信任的数据是一种固有的危险活动,因为传入数据流的内容决定了创建的对象、其字段的值以及它们之间的引用。在许多典型用途中,流中的字节是从未知、不受信任或未经身份验证的客户端接收的。通过仔细构建流,攻击者可以导致恶意执行任意类中的代码。如果对象构造具有改变状态或调用其他操作的副作用,那么这些操作可能会损害应用程序对象、库对象甚至 Java 运行时的完整性。禁用反序列化攻击的关键是防止任意类的实例被反序列化,从而防止直接或间接执行它们的方法。

我们在 Java 9 中引入了反序列化过滤器(JEP 290),使应用程序和库代码能够在反序列化之前验证传入的数据流。此类代码 java.io.ObjectInputFilter 在创建反序列化流(即 a java.io.ObjectInputStream)时提供验证逻辑作为 a 。

依赖流的创建者来明确请求验证有几个限制。这种方法不能扩展,并且很难在代码发布后更新过滤器。它也不能对应用程序中第三方库执行的反序列化操作进行过滤。

为了解决这些限制,JEP 290 还引入了一个 JVM 范围的反序列化过滤器,可以通过 API、系统属性或安全属性进行设置。此过滤器是静态的,因为它在启动时只指定一次。使用静态 JVM 范围过滤器的经验表明,它也有局限性,尤其是在具有库层和多个执行上下文的复杂应用程序中。对每个使用 JVM 范围的过滤器 ObjectInputStream 要求过滤器覆盖应用程序中的每个执行上下文,因此过滤器通常会变得过于包容或过于严格。

更好的方法是以不需要每个流创建者参与的方式配置每个流过滤器。

为了保护 JVM 免受反序列化漏洞的影响,应用程序开发人员需要清楚地描述每个组件或库可以序列化或反序列化的对象。对于每个上下文和用例,开发人员应该构建并应用适当的过滤器。例如,如果应用程序使用特定库来反序列化特定对象群组,则可以在调用库时应用相关类的过滤器。创建类的允许列表并拒绝其他所有内容,可以防止流中未知或意外的对象。封装或其他自然应用程序或库分区边界可用于缩小允许或绝对不允许的对象集。

应用程序的开发人员处于了解应用程序组件的结构和操作的最佳位置。此增强功能使应用程序开发人员能够构建过滤器并将其应用于每个反序列化操作。

如上所述,JEP 290 引入了 per-stream 反序列化过滤器和静态 JVM-wide 过滤器。每当 ObjectInputStream 创建一个时,它的每个流过滤器都会被初始化为静态 JVM 范围的过滤器。如果需要,可以稍后将每个流过滤器更改为不同的过滤器。

这里我们介绍一个可配置的 JVM 范围的过滤器工厂。每当 ObjectInputStream 创建时,它的每个流过滤器都会初始化为通过调用静态 JVM 范围过滤器工厂返回的值。因此,这些过滤器是动态的特定上下文的,与单个静态 JVM 范围的反序列化过滤器不同。为了向后兼容,如果未设置过滤器工厂,则内置工厂将返回静态 JVM 范围的过滤器(如果已配置)。

过滤器工厂用于 Java 运行时中的每个反序列化操作,无论是在应用程序代码、库代码中,还是在 JDK 本身的代码中。该工厂特定于应用程序,应考虑应用程序中的每个反序列化执行上下文。过滤器工厂从 ObjectInputStream 构造函数调用,也从 ObjectInputStream.setObjectInputFilter,参数是当前过滤器和新过滤器。从构造函数调用时,当前过滤器是 null,新过滤器是静态 JVM 范围的过滤器。工厂确定并返回流的初始过滤器。工厂可以使用其他特定于上下文的控件创建复合过滤器,或者只返回静态 JVM 范围的过滤器。如果 ObjectInputStream.setObjectInputFilter 被调用,工厂被第二次调用,并使用第一次调用返回的过滤器和请求的新过滤器。工厂决定如何组合两个过滤器并返回过滤器,替换流上的过滤器。

对于简单的情况,过滤器工厂可以为整个应用程序返回一个固定的过滤器。例如,这里有一个过滤器,它允许示例类,允许 java.base 模块中的类,并拒绝所有其他类:

java  var filter = ObjectInputFilter.Config.createFilter("example.*;java.base/*;!*")

在具有多个执行上下文的应用程序中,过滤器工厂可以通过为每个上下文提供自定义过滤器来更好地保护各个上下文。构建流时,过滤器工厂可以根据当前线程本地状态、调用者的层次结构、库、模块和类加载器来识别执行上下文。此时,用于创建或选择过滤器的策略可以根据上下文选择特定过滤器或过滤器组合。

如果存在多个过滤器,则可以合并它们的结果。组合过滤器的一种有用方法是,如果任何过滤器拒绝反序列化,则拒绝反序列化,如果任何过滤器允许,则允许反序列化,否则保持未定状态。

属性 jdk.serialFilterjdk.serialFilterFactory 可以 在命令行上设置过滤器和过滤器工厂。现有 jdk.serialFilter 属性设置基于模式的过滤器。

jdk.serialFilterFactory 属性是在第一次反序列化之前要设置的过滤器工厂的类名。该类必须是公共的,并且可由应用程序类加载器访问。

为了与 JEP 290 兼容,如果 jdk.serialFilterFactory 未设置属性,则过滤器工厂将设置为提供与早期版本兼容的内置函数。

我们在 ObjectInputFilter.Config 类中定义了两个方法来设置和获取 JVM 范围的过滤器工厂。过滤器工厂是一个有两个参数的函数,一个当前过滤器和一个下一个过滤器,它返回一个过滤器。

```java /** * Return the JVM-wide deserialization filter factory. * * @return the JVM-wide serialization filter factory; non-null */ public static BinaryOperator getSerialFilterFactory();

/** * Set the JVM-wide deserialization filter factory. * * The filter factory is a function of two parameters, the current filter * and the next filter, that returns the filter to be used for the stream. * * @param filterFactory the serialization filter factory to set as the * JVM-wide filter factory; not null */ public static void setSerialFilterFactory(BinaryOperator filterFactory); ```

这个类展示了如何过滤到当前线程中发生的每个反序列化操作。它定义了一个线程局部变量来保存每个线程的过滤器,定义一个过滤器工厂来返回该过滤器,将工厂配置为 JVM 范围的过滤器工厂,并提供一个实用函数来 Runnable 在特定的 per 上下文中运行-线程过滤器。

```java public class FilterInThread implements BinaryOperator {

// ThreadLocal to hold the serial filter to be applied
private final ThreadLocal<ObjectInputFilter> filterThreadLocal = new ThreadLocal<>();

// Construct a FilterInThread deserialization filter factory.
public FilterInThread() {}

/**
 * The filter factory, which is invoked every time a new ObjectInputStream
 * is created.  If a per-stream filter is already set then it returns a
 * filter that combines the results of invoking each filter.
 *
 * @param curr the current filter on the stream
 * @param next a per stream filter
 * @return the selected filter
 */
public ObjectInputFilter apply(ObjectInputFilter curr, ObjectInputFilter next) {
    if (curr == null) {
        // Called from the OIS constructor or perhaps OIS.setObjectInputFilter with no current filter
        var filter = filterThreadLocal.get();
        if (filter != null) {
            // Prepend a filter to assert that all classes have been Allowed or Rejected
            filter = ObjectInputFilter.Config.rejectUndecidedClass(filter);
        }
        if (next != null) {
            // Prepend the next filter to the thread filter, if any
            // Initially this is the static JVM-wide filter passed from the OIS constructor
            // Append the filter to reject all UNDECIDED results
            filter = ObjectInputFilter.Config.merge(next, filter);
            filter = ObjectInputFilter.Config.rejectUndecidedClass(filter);
        }
        return filter;
    } else {
        // Called from OIS.setObjectInputFilter with a current filter and a stream-specific filter.
        // The curr filter already incorporates the thread filter and static JVM-wide filter
        // and rejection of undecided classes
        // If there is a stream-specific filter prepend it and a filter to recheck for undecided
        if (next != null) {
            next = ObjectInputFilter.Config.merge(next, curr);
            next = ObjectInputFilter.Config.rejectUndecidedClass(next);
            return next;
        }
        return curr;
    }
}

/**
 * Apply the filter and invoke the runnable.
 *
 * @param filter the serial filter to apply to every deserialization in the thread
 * @param runnable a Runnable to invoke
 */
public void doWithSerialFilter(ObjectInputFilter filter, Runnable runnable) {
    var prevFilter = filterThreadLocal.get();
    try {
        filterThreadLocal.set(filter);
        runnable.run();
    } finally {
        filterThreadLocal.set(prevFilter);
    }
}

} ```

如果已经设置了特定于流的过滤器,ObjectInputStream::setObjectFilter 则过滤器工厂将该过滤器与下一个过滤器组合。如果任一过滤器拒绝一个类,则该类将被拒绝。如果任一过滤器允许该类,则该类被允许。否则,结果未定。

这是使用 FilterInThread 该类的一个简单示例:

```java // Create a FilterInThread filter factory and set var filterInThread = new FilterInThread(); ObjectInputFilter.Config.setSerialFilterFactory(filterInThread);

// Create a filter to allow example.* classes and reject all others var filter = ObjectInputFilter.Config.createFilter("example.;java.base/;!*"); filterInThread.doWithSerialFilter(filter, () -> { byte[] bytes = ...; var o = deserializeObject(bytes); }); ```

其他变化

恢复始终严格的浮点语义

使浮点运算始终严格,而不是同时具有严格的浮点语义(strictfp)和略有不同的默认浮点语义。这将恢复语言和 VM 的原始浮点语义,匹配 Java SE 1.2 中引入严格和默认浮点模式之前的语义。

1990 年代后期改变平台默认浮点语义的动力源于原始 Java 语言和 JVM 语义之间的不良交互以及流行的 x86 架构的 x87 浮点协处理器指令集的一些不幸特性. 在所有情况下匹配精确的浮点语义,包括非正规操作数和结果,需要大量额外指令的开销。在没有上溢或下溢的情况下匹配结果可以用更少的开销来实现,这大致是 Java SE 1.2 中引入的修改后的默认浮点语义所允许的。

但是,从 2001 年左右开始在奔腾 4 和更高版本的处理器中提供的 SSE2(流式 SIMD 扩展 2)扩展可以以直接的方式支持严格的 JVM 浮点运算,而不会产生过多的开销。

由于英特尔和 AMD 长期以来都支持 SSE2 和更高版本的扩展,这些扩展允许自然支持严格的浮点语义,因此不再存在具有不同于严格的默认浮点语义的技术动机。

此 JEP 将修改的接口包括浮点表达式覆盖范围内的 Java 语言规范(请参阅 JLS部分 4.2.3浮点类型、格式和值、5.1.13 值集转换、15.4 *FP-strict Expressions、对第 15 章后面其他部分的许多小更新)和 Java 虚拟机规范的类似部分(JVMS 2.3.2浮点类型、值集和值,第 2.8.2 节浮点模式,2.8.3值集)转换,以及对个别浮点指令的许多小更新)。值集和值集转换的概念将从 JLS 和 JVMS 中删除。JDK 中的实现更改将包括更新 HotSpot 虚拟机,使其永远不会在允许扩展指数值集的浮点模式下运行(这种模式必须存在于 strictfp 操作中)并更新 javac 以发出新的 lint 警告以防止不必要的使用的 strictfp 修饰符。

增强型伪随机数生成器

为伪随机数生成器(PRNG)提供新的接口类型和实现,包括可跳转的 PRNG 和额外的一类可拆分 PRNG 算法(LXM)。

目标:

  • 使在应用程序中交替使用各种 PRNG 算法变得更容易。
  • 通过提供 PRNG 对象流更好地支持基于流的编程。
  • 消除现有 PRNG 类中的代码重复。
  • 小心地保留 class 的现有行为 java.util.Random

使用遗留的 PRNG 类 RandomThreadLocalRandomSplittableRandom,很难用其他算法替换应用程序中的任何一个,尽管它们都支持几乎相同的方法集。例如,如果一个应用程序使用 class 的实例 Random,它必然会声明 type 的变量 Random,它不能保存 class 的实例 SplittableRandom;更改要使用的应用程序 SplittableRandom 需要更改用于保存 PRNG 对象的每个变量(包括方法参数)的类型。一个例外是它 ThreadLocalRandomRandom 的子类,纯粹是为了允许类型的变量 Random 保存的实例 ThreadLocalRandom,但 ThreadLocalRandom 几乎覆盖了 的所有方法 Random。接口可以轻松解决这个问题。

传统类 RandomThreadLocalRandom 以及 SplittableRandom 所有支持等方法,nextDouble()nextBoolean() 以及流产生方法,如 ints()longs(),但他们拥有完全独立的,几乎复制和粘贴的方式工作。重构此代码将使其更易于维护,此外,文档将使第三方更容易创建新的 PRNG 类,这些类也支持相同的完整方法套件。

2016 年,测试揭示了 class 使用的算法中的两个新弱点 SplittableRandom。一方面,相对较小的修订可以避免这些弱点。另一方面,还发现了一类新的可拆分 PRNG 算法(LXM),其速度几乎一样快,甚至更容易实现,并且似乎完全避免了容易出现的三类弱点 SplittableRandom

能够从 PRNG 获取 PRNG 对象流使得使用流方法表达某些类型的代码变得更加容易。

文献中有许多 PRNG 算法不是可拆分的,而是可跳跃的(也许也是可跳跃的,即能够进行非常长的跳跃和普通跳跃),这一特性与拆分完全不同,但也有助于支持流PRNG 对象。过去,很难在 Java 中利用这一特性。可跳转 PRNG 算法的示例是 Xoshiro256 和 Xoroshiro128+。

新的MacOS渲染管线

使用 Apple Metal API 为 macOS 实现 Java 2D 内部渲染管道,作为现有管道的替代方案,现有管道使用已弃用的 Apple OpenGL API。

目标:

  • 为使用 MacOS Metal 框架的 Java 2D API 提供功能齐全的渲染管道。
  • 如果 Apple 从未来版本的 MacOS 中删除已弃用的 OpenGL API,请做好准备。
  • 确保新管道到 Java 应用程序的透明度。
  • 确保实现与现有 OpenGL 管道的功能奇偶校验。
  • 在选定的实际应用程序和基准测试中提供与 OpenGL 管道一样好或更好的性能。
  • 创建适合现有 Java 2D 管道模型的干净架构。
  • 与 OpenGL 管道共存,直到它过时。

两个主要因素促使在 MacOS 上引入新的基于 Metal 的渲染管道:

  • Apple 于 2018 年 9 月在 MacOS 10.14 中弃用了 OpenGL 渲染库。MacOS 上的 Java 2D 完全依赖 OpenGL 进行其内部渲染管道,因此需要新的管道实现。
  • Apple 声称 Metal 框架(它们替代 OpenGL)具有卓越的性能。对于 Java 2D API,通常是这种情况,但有一些例外。

大多数 Java GUI 应用程序是使用 Swing UI 工具包编写的,该工具包通过 Java 2D API 呈现。在内部,Java 2D 可以使用软件渲染和屏幕上的 blit,也可以使用特定于平台的 API,例如 Linux 上的 X11/Xrender、Windows 上的 Direct3D 或 macOS 上的 OpenGL。这些特定于平台的 API 通常提供比软件渲染更好的性能,并且通常会减轻 CPU 的负担。Metal 是用于此类渲染的新 MacOS 平台 API,取代了已弃用的 OpenGL API。(该名称与 Swing “金属”外观和感觉无关;这只是巧合。)

我们创建了大量新的内部实现代码来使用 Metal 框架,就像我们已经为其他特定于平台的 API 所做的那样。虽然很容易适应现有框架,但新代码在使用图形硬件方面更加现代,使用着色器而不是固定功能管道。这些更改仅限于特定于 MacOS 的代码,甚至只更新了 Metal 和 OpenGL 之间共享的最少量代码。我们没有引入任何新的 Java API,也没有改变任何现有的 API。

Metal 管道可以与 OpenGL 管道共存。当图形应用程序启动时,会选择其中一个。目前,OpenGL 仍然是默认设置。仅当在启动时指定或 OpenGL 初始化失败时才使用 Metal,就像在没有 OpenGL 支持的未来版本的 MacOS 中一样。

在集成此 JEP 时,Apple 尚未删除 OpenGL。在此之前,应用程序可以通过 -Dsun.java2d.metal=truejava 命令行上指定来选择加入 Metal。我们将在未来的版本中将 Metal 渲染管线设为默认。

在集成到 JDK 之前,我们在 Project Lanai 中对这个 JEP 进行了工作。

MacOS/AArch64端口

将 JDK 移植到 MacOS/AArch64。Apple 宣布了一项将其 Macintosh 计算机系列从 x64 过渡到 AArch64 的长期计划。因此,我们希望看到对 JDK 的 MacOS/AArch64 端口的广泛需求。

尽管可以通过 MacOS 的内置 Rosetta 2 转换器在基于 AArch64 的系统上运行 JDK 的 MacOS/x64 版本,但该翻译几乎肯定会带来显着的性能损失。

Linux 的 AArch64 端口(JEP 237)已经存在,Windows 的 AArch64 端口(JEP 388)的工作正在进行中。我们希望通过使用条件编译(在 JDK 的端口中很常见)来重用来自这些端口的现有 AArch64 代码,以适应低级约定的差异,例如应用程序二进制接口(ABI)和保留的处理器寄存器集。

MacOS/AArch64 禁止内存段同时可执行和可写,这一策略称为 write-xor-execute (W^X)。HotSpot VM 会定期创建和修改可执行代码,因此此 JEP 将在 HotSpot 中为 macOS/AArch64 实现 W^X 支持。

弃用Applet API

弃用 Applet API 以进行删除。它基本上无关紧要,因为所有 Web 浏览器供应商都已取消对 Java 浏览器插件的支持或宣布了这样做的计划。 Java 9 中的 JEP 289 先前已弃用 Applet API,但并未将其删除。

强封装JDK内部

强烈封装 JDK 的所有内部元素,除了关键的内部 API,如 sun.misc.Unsafe。不再可能通过单个命令行选项来放松内部元素的强封装,就像在 JDK 9 到 JDK 16 中那样。

这个 JEP 是 JEP 396 的继承者,它将 JDK 从默认的宽松强封装转换为默认强封装,同时允许用户根据需要返回到轻松的姿势。本 JEP 的目标、非目标、动机、风险和假设部分与 JEP 396 的部分基本相同,但为了读者的方便在此处复制。

删除RMI激活

删除远程方法调用(RMI)激活机制,同时保留 RMI 的其余部分。

RMI 激活机制已过时且已废弃。它已被 Java SE 15 中的 JEP 385 弃用。没有收到针对该弃用的评论。请参阅 JEP 385 了解完整的背景、原理、风险和替代方案。

Java EE 平台包含一项称为 JavaBeans Activation Framework(JAF)的技术。作为 Eclipse EE4J 计划的一部分,它后来更名为 Jakarta Activation。JavaBeans Activation 和 Jakarta Activation 技术与 RMI Activation 完全无关,它们不受从 Java SE 中删除 RMI Activation 的影响。

删除实验性AOT和JIT编译器

删除实验性的基于 Java 的提前(AOT)和即时(JIT)编译器。该编译器自推出以来几乎没有什么用处,维护它所需的工作量很大。保留实验性的 Java 级 JVM 编译器接口(JVMCI),以便开发人员可以继续使用外部构建的编译器版本进行 JIT 编译。

提前编译(该 jaotc 工具)已通过 JEP 295 作为实验性功能合并到 JDK 9 中。该 jaotc 工具使用 Graal 编译器,它本身是用 Java 编写的,用于 AOT 编译。

Graal 编译器通过 JEP 317 在 JDK 10 中作为实验性 JIT 编译器提供。

自从引入这些实验性功能以来,我们几乎没有看到它们的使用,并且维护和增强它们所需的工作量很大。这些特性没有包含在 Oracle 发布的 JDK 16 版本中,并且没有人抱怨。

弃用安全管理器以进行删除

弃用安全管理器以在未来版本中移除。安全管理器可追溯到 Java 1.0。多年来,它一直不是保护客户端 Java 代码的主要手段,也很少用于保护服务器端代码。为了推动 Java 向前发展,我们打算弃用安全管理器,以便与旧 Applet API(JEP 398)一起删除。

外部函数和内存API(孵化中)

介绍一个 API,Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过有效调用外部函数(即 JVM 之外的代码),以及安全地访问外部内存(即不由 JVM 管理的内存),API 使 Java 程序能够调用本地库和处理本地数据,而没有 JNI。

本 JEP 中提出的 API 是两个孵化 API 的演变:外部内存访问 API 和外部链接器 API。Foreign-Memory Access API 最初由 JEP 370 提出,并于 2019 年末针对 Java 14 作为孵化 API;它由Java 15 中的 JEP 383 和Java 16 中的 JEP 393 重新孵化。外部链接器 API 最初由 JEP 389 提出,并于 2020 年末针对 Java 16,也作为孵化 API。


原文地址:https://blog.csdn.net/weixin_45254062/article/details/140580824

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!