概述
JDK 17 于 2021 年 9 月 14 日正式发布。
JDK 17 是自 2018 年JDK 11
后的第二个长期支持版本,支持到 2029 年 9 月,支持时间长达8
年。下一个长期支持版本是 JDK 21
,时间为2023 年 9 月,这次长期支持版本发布计划改了,不再是原来的 3 年一次,而是改成了 2 年一次!非长期支持版本还是半年发一次不变。
JDK 17二进制文件在Oracle
免费条款和条件许可下,可以在生产中免费使用(参考:Oracle Releases Java 17),也可以免费重新分发。开源协议为NFTC
(Oracle No-Fee Terms and Conditions
)。
Java 17
及以后的版本可以免费使用了,包括商用,更详细的条款可以阅读:Oracle No-Fee Terms and Conditions (NFTC) License Agreement
Java
的商用收费始于Java 8
从2019年1月份开始,Oracle JDK
开始对Java SE 8
之后的版本开始进行商用收费,确切的说是8u201/202
之后的版本。如果你用 Java 开发的功能如果是用作商业用途的,如果还不想花钱购买的话,能免费使用的最新版本是8u201/202
。当然如果是个人客户端或者个人开发者可以免费试用 Oracle JDK
所有的版本。另外Oracle
官方其实还提供了一个完全免费开源的JDK
版本—— Open JDK
,这是免费使用的。中间版本
商用收费情况
Java
的商用收费始于Java 8
,Oracle JDK
宣布从2019年1月份开始对Java SE 8
之后的版本开始进行商用收费,确切的说是8u201/202
之后的版本。如果你用 Java 开发的功能如果是用作商业用途的,如果还不想花钱购买的话,能免费使用的最新版本是8u201/202
。当然如果是个人客户端或者个人开发者可以免费试用 Oracle JDK
所有的版本。另外Oracle
官方其实还提供了一个完全免费开源的JDK
版本—— Open JDK
,这是免费使用的。
决定是否收费首先得看JDK使用的是什么协议?
-
BCL协议:即
Oracle Binary Code License Agreement
,协议规定你可以使用JDK
,但是不能进行修改。私用和商用都可以,但是JDK
中的某些商业特性,是需要付费才可以使用的。 -
OTN协议:即
Oracle Technology Network License Agreement
,目前新发布的JDK
用的都是这个协议,可以私用,商用需要付费。
版本说明
-
Java 8
以下版本,仍可免费使用。 -
Java 8
(LTS) 部分免费,8u201/202
及之前的版本是免费的,之后的商用收费。 -
Java 9
是免费的,过渡版本且不再更新不建议使用。 -
Java 10
是免费的,过渡版本且不再更新不建议使用。 -
Java 11
(LTS) 开始Oracle JDK
商用收费,同时提供免费版的Open JDK
,下载地址。 -
Java 12
、Java 13
、Java 14
、Java 15
、Java 16
,全版本商用收费。 -
Java 17
(LTS) 开始,再次可以免费使用。 -
免费的JDK有 OpenJDK 、 AdoptOpenJDK 、 Amazon Corretto 、 Azul Zulu 、 BellSoft 、 IBM 、 jClarity 、 Red Hat 、 SAP 、 阿里巴巴 Dragonwell等。
JEP(Java Enhancement Proposal)Java增强提案
CSR(Compatibility & Specification Review) 兼容性和规范审查
变动说明
官网:
Java Platform, Standard Edition Java Language Updates, Release 17
JDK 17 Release Notes, Important Changes, and Information
https://blogs.oracle.com/java/post/the-arrival-of-java-17
更多参考:
JDK 17 Documentation - Home 更多版本:Java Platform, Standard Edition Documentation - Releases
Java Platform, Standard Edition Oracle JDK Migration Guide, Release 17
重要变更和信息
JDK 17 包含 14 个 新特性 ,分别为:
-
JEP 306: Restore Always-Strict Floating-Point Semantics 恢复始终严格的浮点语义
-
JEP 356: Enhanced Pseudo-Random Number Generators 增强型伪随机数生成器
-
JEP 382: New macOS Rendering Pipeline 新的 macOS 渲染管线
-
JEP 391: macOS/AArch64 Port macOS/AArch64移植
-
JEP 398: Deprecate the Applet API for Removal 移除 Applet API
-
JEP 403: Strongly Encapsulate JDK Internals 强封装JDK内部API
-
JEP 406: Pattern Matching for switch (Preview) Switch 的模式匹配(预览)
-
JEP 407: Remove RMI Activation 删除 RMI 激活
-
JEP 409: Sealed Classes 密封的类和接口(正式特性)
-
JEP 410: Remove the Experimental AOT and JIT Compiler 移除实验性的 AOT 和 JIT 编译
-
JEP 411: Deprecate the Security Manager for Removal 弃用安全管理器
-
JEP 412: Foreign Function & Memory API (Incubator) 外部函数与内存 API(孵化特性)
-
JEP 414: Vector API (Second Incubator) 向量API(第二次孵化)
-
JEP 415: Context-Specific Deserialization Filters 上下文特定的反序列化过滤器
而其中与开发过程中直接相关的特性主要包括:JEP 406(Switch 表达式的模式匹配)、JEP 409(密封的类和接口)等。
下载地址
你可以从这个链接下载Oracle JDK
版本,更多版本下载。
也可以从这个链接下载生产就绪的OpenJDK
版本。文件为压缩包,解压并设置环境变量就可以使用。
Java17新特性总结
1、JEP 409:密封的类和接口(正式特性)
功能进化
Java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 15 | 预览特性 | JEP 360 | 引入了密封类作为预览特性。 |
Java 16 | 预览特性 | JEP 397 | 第二次预览 |
Java 17 | 正式特性 | JEP 409 | 成为正式特性 |
密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。
继承,作为面向对象语言的三大特性之一,我们工作过程中经常使用,可以重写父类的方法。我们可以通过继承(extend
)来实现类的能力复用、扩展与增强。但有的时候,有些能力我们不希望被继承了去做一些不可预知的扩展。所以,我们需要对继承关系有一些限制的控制手段。而密封类的作用就是限制类的继承。
通常开发项目时,我们会先将接口提供出来,然后根据情况给出不同的基础实现类,子类再基础这些基础实现类进行扩展,我们可能并不希望子类直接继承接口,当然直接继承接口的写法从代码上看没有任何问题,但存在安全隐患。一般我们会通过开发约束对,这样的情况说一些要求,但是这样并不能杜绝这类问题。
限制手段
对于继承能力的控制,目前主要通过下面两种方式控制:
-
final
修饰类,这样类就无法被继承了 -
无修饰词修饰的类(即去掉
public
修饰符,一般类修饰符是public
),可以限制该类只能被同一个包下的类继承。
这两种限制方式的粒度都非常粗,对于一些要求比较细致的场景,是无法满足的。
密封类
为了进一步增强继承的限制能力,Java 15 引入密封类来精确控制类的继承问题 ,目前版本为预览特性。
什么是密封类
密封类的主要目的是提供一种更加精确地控制类继承的方法,通过这种方式,类的设计者可以指定一个类它能够被哪些类继承,它增强了类的封装性和安全性。由于密封类限制了类的继承,所以它使得代码更加可预测和易于维护。
-
密封类(接口)用
sealed
修饰,则它的所有子类都必须在同一个模块或者包内,并且这些子类必须被显式地声明为该密封类的直接子类。 -
密封类(接口)的子类可以被声明为
non-sealed
(非密封的)或final
(最终的)。non-sealed
的子类可以被进一步继承,而final
的子类则不能。 -
密封类(接口)使用
permits
来指定它的子类。
示例代码
这里我们以诗人为例,简化一下,我们这里只讨论汉朝诗人、唐朝诗人、宋朝诗人,代码如下:
// 诗人基类 public class Poet { } // 汉朝诗人 public class HanPoet extends Hero{ } // 唐朝诗人 public class TangPoet extends Poet{ } // 宋朝诗人 public class SongPoet extends Hero{ }
接下来我们每个类别下面定义2个诗人:
-
汉朝诗人(HanPoet):司马相如(SiMaXiangRu)、班固(BanGu)、
-
唐朝诗人(TangPoet):李白(Libai)、杜甫(DuFu)
-
宋朝诗人(SongPoet):苏轼(SuShi)、陆游(LuYou)
其中李白(Libai)
继承自唐朝诗人(TangPoet)
,我们可以为唐朝诗人做一些公共处理,比如朝代是唐朝,但是有没有这种可能,有程序猿把李白的父类定义为诗人(Poet)
,那么我们为唐朝诗人定义的那些处理,李白就需要全部重新实现。这显然破坏了继承的实用性。
-
使用
sealed
修饰Poet
,permits
限定子类为:HanPoet
、TangPoet
、SongPoet
,只允许这3个类继承,如下:
// 英雄基类,限制子类为:汉朝诗人(HanPoet)、唐朝诗人(TangPoet)、宋朝诗人(SongPoet) public sealed class Poet permits HanPoet,TangPoet,SongPoet { }
-
第二层基类,继续使用
sealed
修饰
// 汉朝诗人,限制子类为:司马相如(SiMaXiangRu)、班固(BanGu) public sealed class HanPoet extends Hero permits SiMaXiangRu,BanGu{ } // 唐朝诗人,限制子类为:李白(Libai)、杜甫(DuFu) public sealed class TangPoet extends Hero permits Libai,DuFu{ } // 宋朝诗人,限制子类为:苏轼(SuShi)、陆游(LuYou) public sealed class SongPoet extends Hero permits SuShi,LuYou{ }
-
第三层为具体诗人,他们继承第二层的诗人类型,使用
extends
继承即可,同时需要表示为non-sealed
或final
,由于我们不希望类再往下了,所以定义为final
:
public final class SiMaXiangRu extends HanPoet{ } public final class Libai extends TangPoet{ } public final class SuShi extends SongPoet{ }
这样,子类就不能随便继承父类了。
2、JEP 406: Switch 的模式匹配(预览特性)
JEP 406 (specification
)
这是一个预览特性
功能进化
switch
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 5 | 首次引入,仅支持整型数据类型(如 byte , short , char , 和 int )及枚举类型 | ||
Java 7 | 支持 String 类型 | ||
Java 12 | 预览特性 | JEP 325 | 支持Switch 表达式(箭头函数) |
Java 13 | 预览特性 | JEP 354 | 加入 yield 语句来替代 break 语句,用于从 switch 表达式返回值 |
Java 14 | 正式特性 | JEP 361 | 前2个版本的新特性转为正式特性 |
Java 17 | 预览特性 | JEP 406 | 引入Switch 的模式匹配作为预览特性 |
在 Java 16 中, JEP 394
将 instanceof
的模式匹配发布为正式属性。虽然可以不需要强制转换了,但是仍然需要大量的 if...else
。而 Switch 表达式虽然简化了if...else
,但是它无法像instanceof
一样不需要强制转换。为了解决这个痛点,Java 17
引入模式匹配的Switch
表达式特性 ,目前该特性为预览特性。
该特性扩展了 switch 表达式和语句,允许它们使用模式匹配,这就意味着我们可以在 switch
的 case 标签中使用模式,如类型模式,使得代码更加灵活和表达性更强。而且也无需进行显式的类型转换了。例如,可以使用 case Integer i
这样的语法来匹配并自动转换类型。
但是,不知道小伙伴们注意没有,Switch
表达式只有一种类型,比如:我有一个诗人类(Poet
),它有3个实现类:唐朝诗人(TangPoet
)、宋朝诗人(SongPoet
)、汉朝诗人(HanPoet
),我要根据诗人的类型进行不同处理 :
Poet poet = ... // 诗人 switch (poet.getClass().getName()) { case "my.poet.TangPoet": TangPoet tp = (TangPoet) obj; // 处理唐朝诗人 break; case "my.poet.SongPoet": SongPoet sp = (SongPoet) obj; // 处理宋朝诗人 break; case "my.poet.HanPoet": HanPoet hp = (HanPoet) obj; // 处理汉朝诗人 break; // 其他类型 }
这个强转显然比较麻烦。所以,参考Java 17
中,参考instanceof
的模式匹配,为switch
表达式引入了模式匹配功能作为预览特性。
Switch 模式匹配
在 Java 17 中,switch
表达式允许使用模式匹配来处理对象类型,这样就可以直接在 switch
语句中检查和转换类型,而不需要额外的 if...else
结构和显式类型转换。
case
后面可以跟的标签主要有:
-
类型标签
-
null标签
-
守卫标签
-
enum或常量值
类型标签
允许在 switch
语句的 case 分支中直接匹配对象的类型。例如,case String s
允许你在该分支中直接作为字符串类型的 s
来使用,避免了显式的类型检查和强制类型转换。
举个例子:
@Test public void switchTest() { // 不是用switch根据类型判断 Object[] objects = { "Hello", "Java", "17", 666, 0.618 }; for (Object obj : objects) { if (obj instanceof Integer v) { System.out.printf("为整型 :%s %n", v); } else if (obj instanceof Float v) { System.out.printf("为Float:%s %n", v); } else if (obj instanceof Double v) { System.out.printf("为Double:%s %n", v); } else if (obj instanceof String v) { System.out.printf("为字符串:%s %n", v); } else { System.out.printf("其他类型:%s %n", obj); } } }
我们用 Switch
表达式来改造下:
@Test public void switchTest() { Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" }; for (Object obj: objects) { switch (obj) { case Integer v -> System.out.println("为整数型:" + v); case Float v -> System.out.println("为浮点型:" + v); case Double v -> System.out.println("为双精度浮点数:" + v); case String v -> System.out.println("为字符串:" + v); default -> System.out.println("其他类型:" + obj); } } }
相比上面的 if...else
简洁了很多。同时在 Java 17
之前,Switch
选择器表达式只支持特定类型,即基本整型数据类型byte
、short
、char
和int
;对应的装箱形式Byte
、Short
、Character
和Integer
;String
类;枚举类型。现在有了类型模式,Switch
表达式可以是任何类型啦。
null标签
当switch
允许任何引用类型的选择器表达式,那么我们需要留意null
的情况,在Java17
之前,向switch
语句传递一个null
值,会抛出一个NullPointerException
,现在可以通过类型模式,将 null 检查作为一个单独的case标签来处理,如下:
@Test public void switchTest() { Object[] objects = { "Hello", "Java", "17", 142857, 0.618 }; for (Object obj: objects) { switch (obj) { case Integer v -> System.out.println("为整数型:" + v); case Float v -> System.out.println("为浮点型:" + v); case Double v -> System.out.println("为双精度浮点数:" + v); case String v -> System.out.println("为字符串:" + v); case null -> System.out.println("为空值"); default -> System.out.println("其他类型:" + obj); } } }
case null
可以直接匹配值为 null
的情况。
守卫标签
与匹配常量值的case
标签不同,模式case
标签可以对应多个变量值。这通常会导致switch
规则右侧出现条件语句。
根据字符串长度判断诗句是五言绝句还是七言绝句,代码如下:
@Test public void switchCaseCaseTest() { String[] poems = { "千山鸟飞绝", "春城无处不飞花", "红豆生南国", "二月春风似剪刀","念奴娇" }; for (String poem : poems) { switch (poem) { case null -> System.out.println("为空值"); case String s -> { if (s.length() == 5) System.out.printf("五言绝句:%s%n", s); else if (s.length() == 7) System.out.printf("七言绝句:%s%n", s); else System.out.printf("不知道是啥:%s%n", s); } } } }
这里的问题是,使用单一模式(即类型)来区分case
就只能判断一种情况。我们只能在模式匹配中再通过if……else
判断来区分不同的情况,来对一个模式的细化。这时,我们可以是使用switch
中的when
子句指定模式case
标签的条件,例如,case String s when if (s.length() == 5)
。表示当类型为String
并且字符串长度为5的时候,我们将这种case
标签称为守卫case
标签,将布尔表达式称为保护。
@Test public void switchCaseCaseTest() { String[] poems = { "千山鸟飞绝", "春城无处不飞花", "红豆生南国", "二月春风似剪刀","念奴娇" }; for (String poem : poems) { switch (poem) { case null -> System.out.println("为空值"); case String s when s.length() == 5 -> System.out.printf("五言绝句:%s%n", s); case String s when s.length() == 7 -> System.out.printf("七言绝句:%s%n", s); case String s -> System.out.printf("不知道是啥:%s%n", s); //剩余情况,仍然走这个 } } }
使用守卫标签,我们可以编写更灵活和表达性强的代码。
3、JEP 306:恢复始终执行严格模式的浮点定义
Java 最初只有严格的浮点语义,但从JDK 1.2
开始,为了适应当时硬件架构的限制,默认情况下允许这些严格语义中的细微变化,而现在这些都没有必要了。
4、JEP 356:增强型伪随机数生成器。
为伪随机数生成器 (PRNG) 提供新的接口类型和实现。
Java 17 之前的随机数生成
在 Java 17 之前,Java 的随机数生成主要依赖于下面两个核心类:
-
java.util.Random
-
java.security.SecureRandom
Random
该类是最最基本的伪随机数生成器,它用于生成一系列不完全是真正随机的数字。Random
提供了多种方法来生成不同类型的随机数,包括整数、长整数、浮点数等。
-
生成随机整数
Random rand = new Random(); int randomInt = rand.nextInt(50); // 生成一个0到49之间的随机整数
-
生成随机浮点数
Random rand = new Random(); double randomDouble = rand.nextDouble(); // 生成一个0.0到1.0之间的随机浮点数
java.util.Random
使用起来很简单,但是它有两个很明显的缺陷。
-
具备可观测性:如果知道种子和算法,完全可以生成相同的随机序列。这在需要高安全性的随机数生成(如加密)时是不合适的。不是真的随机。
-
线程不安全:在多线程环境下,多个线程共享一个
Random
实例可能导致竞争条件和数据不一致。
ThreadLocalRandom
ThreadLocalRandom
是一个适用于多线程环境下生成随机数的生成器,它通过为每个线程提供一个独立的随机数生成器实例来解决线程安全问题。
ThreadLocalRandom
使用线程局部变量(Thread-Local)的概念,每个线程访问 ThreadLocalRandom
时,实际上是访问它自己的一个独立实例,这就着不同线程之间的随机数生成器是完全隔离的,而且他们各自内部的种子是完全隔离的,这就保证了随机数生成的独立性,不会因为其他线程的操作而受到影响。
使用方法:
ThreadLocalRandom random = ThreadLocalRandom.current(); // 获取 ThreadLocalRandom 实例 int randomInt = random.nextInt(10, 50); // 生成10到49的随机整数 double randomDouble = random.nextDouble(1.0, 5.0); // 生成1.0到5.0的随机浮点数
虽然 ThreadLocalRandom
解决了 Random
多线程的问题,但是它依然是基于线性同余生成器,导致它生成的随机数序仍然是可预测的。
SecureRandom
由于 java.util.Random
生成的随机数具备可预测性,所以它不适用一些安全要求较高的场景。java.security.SecureRandom
是 Java 提供的一个用于生成加密强度随机数的类,它是 java.security
包的一部分,专门为需要高安全性的应用场景设计,比如加密、安全令牌生成、会话密钥以及数字签名应用。
SecureRandom
生成的随机数具有高安全性,这是因为它使用了更加复杂和不可预测的算法,而且这些算法通常都是基于操作系统提供的随机性源,例如 Unix/Linux 系统的 /dev/random
和 /dev/urandom
,或 Windows 的 CryptGenRandom API
。所以它更加适用于加密和安全相关的领域。
当然,我们也可以手动设置种子,但是一般不推荐这样做,因为会降低随机数的不可预测性。
SecureRandom
也提供了多种方法来生成不同类型的随机数:
SecureRandom secureRandom = new SecureRandom(); int randomInt = secureRandom.nextInt(); double randomDouble = secureRandom.nextDouble();
我们还可以指定特定的算法来创建 SecureRandom
实例:
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
虽然 SecureRandom
生成的随机数具有强大的安全性,但也恰恰如此,导致SecureRandom
在生成随机数时的性能开销比普通的随机数生成器更高。而且在某些情况下,SecureRandom
可能会因为等待足够的熵(随机性)而导致阻塞,尤其是在使用 /dev/random
作为随机性源的系统上。
Java 17 的随机数生成
Java 17 为随机数提供了一个全新的接口 RandomGenerator
,该接口是 Java 生成随机数的顶层接口,用于定义所有伪随机数生成器的标准方法。它是所有新的和旧的随机数的顶层接口,包括 java.util.Random
。
RandomGenerator
提供了两类生成随机数的方法:
-
生成随机数:例如
nextInt()
、nextInt(0, 100)
、nextLong()
-
生成随机数流:例如
ints()
、longs()
,这些方法返回的是一个 Stream,对于生成大量随机数比较有用。
RandomGenerator
实例的创建方法如下:
-
使用
RandomGenerator.getDefault()
这是获取 RandomGenerator
实例最简单的方式,它返回默认的RandomGenerator
实例,适用于大多数用途。
RandomGenerator randomGenerator = RandomGenerator.getDefault();
-
使用
RandomGenerator.of(String name)
RandomGenerator randomGenerator = RandomGenerator.of("Xoshiro256PlusPlus"); //生成器名称
-
使用工厂类
RandomGeneratorFactory
创建
RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.of("Xoshiro256PlusPlus"); //随机数类型 RandomGenerator randomGenerator = factory.create();
得到了 RandomGenerator
实例,就可以调用对应的方法获取对应的随机数了。
RandomGeneratorFactory
RandomGeneratorFactory
是创建 RandomGenerator
实例的工具类,可以用它来创建不同类型的 RandomGenerator
实例,包括但不限于传统的线性同余生成器、梅森旋转算法(Mersenne Twister)、Xoroshiro128++
算法等。同时它还允许用户根据需求定制随机数生成器的行为,比如设置种子或选择特定的算法。
RandomGeneratorFactory
的核心 API 如下:
-
of(String name)
:根据指定的名称创建一个RandomGenerator
实例。 -
all()
:返回所有可用的RandomGeneratorFactory
实例。 -
getDefault()
:返回默认的RandomGeneratorFactory
实例。 -
create()
:使用工厂的配置创建一个新的RandomGenerator
实例。
下面演示如何使用 RandomGeneratorFactory
创建并使用一个随机数生成器:
@Test public void randomGeneratorFactoryTest() { // 索取所有可用的 随机数类型 RandomGeneratorFactory.all().forEach(x -> { System.out.println(x.name()); }); /*L32X64MixRandom L64X128MixRandom L64X128StarStarRandom L64X256MixRandom L64X1024MixRandom L128X128MixRandom L128X256MixRandom L128X1024MixRandom SecureRandom Xoshiro256PlusPlus Random Xoroshiro128PlusPlus SplittableRandom */ // 获取一个类型为 Xoroshiro128PlusPlus 的随机数生成器工厂 RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.of("Xoroshiro128PlusPlus"); // 使用工厂创建一个随机数生成器 RandomGenerator randomGenerator = factory.create(); // 生成 10 个随机数 randomGenerator.ints().limit(10).forEach(System.out::println); }
下面是 Java 17 提供 11 个不同的随机数生成器:
类名 | 使用的算法 | 特点 |
---|---|---|
L32X64MixRandom | LXM 算法 | 平衡性能和随机性,适用于多种通用应用 |
L32X64StarStarRandom | StarStar算法 | 提供较好的随机性和性能,适用于需要较快随机数生成的场景 |
L64X128MixRandom | LXM 算法 | 提供更长的周期和更高的随机性,适合更复杂的随机数生成需求 |
L64X128StarStarRandom | StarStar算法 | 结合了较长周期和良好的随机性,适用于复杂应用 |
L64X256MixRandom | LXM 算法 | 长周期,高随机性,适合于需求严格的随机数生成场景 |
L64X1024MixRandom | LXM 算法 | 高随机性,适用于特别需要长周期和高随机性的应用 |
L128X128MixRandom | LXM 算法 | 极长周期,高随机性,适合于高随机质量要求的应用 |
L128X256MixRandom | LXM 算法 | 提供极长的周期和优秀的随机性,适用于极高随机质量要求的应用 |
L128X1024MixRandom | LXM 算法 | 极高的随机性和更大的状态空间,适用于对随机数质量有极端要求的场合 |
Xoshiro256PlusPlus | Xoshiro算法 | 高性能,适用于需要快速、高质量随机数的应用 |
Xoroshiro128PlusPlus | Xoroshiro算法 | 提供良好的性能和随机性平衡,适用于多种应用场景 |
5、JEP 382:新的 macOS 渲染管道
JEP 338 (client-libs/2d
)
使用 Apple Metal API 为 macOS 实现了 Java 2D 渲染管道。新管道减少了 JDK 对已弃用的 Apple OpenGL API 的依赖。
6、JEP 391:macOS/AArch64移植
JEP 391 (hotspot/compiler
)
macOS 11.0现在支持AArch64体系结构。这个JEP实现了对JDK中macos-arch64平台的支持。添加的功能之一是支持W^X(写入或执行)内存。它仅针对macos-arch64启用,并可以在某个时候扩展到其他平台。JDK既可以在Intel机器上交叉编译,也可以在基于Apple M1的机器上编译。
7、JEP 398:弃用 Applet API。
JEP 398 (client-libs/java.awt
)
Applet 是一种运行在 Web 浏览器内的 Java 程序,早就过时了。
8、JEP 407:删除了远程方法调用 (RMI) 激活机制
JEP 407 (core-libs/java.rmi
)
9、JEP 410:删除实验性的 AOT 和 JIT 编译器,
JEP 410(hotspot/compiler
)
这两个实验功能并没有被广泛使用,删除不再维护。
10、JEP 411:弃用安全管理器。
JEP 380 (security-libs/java.security
)
11、JEP 403:强封装JDK 内部API
限制外部对 JDK 内部类进行访问,此更改会使应用程序更安全,并减少对非标准、内部 JDK 实现细节的依赖。
12、JEP 412:外部函数和内存 API(孵化特性)
JEP 412 (core-libs
)
孵化阶段。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 孵化特性 | JEP 370 | 引入了外部内存访问 API作为孵化特性 |
Java 15 | 第二次孵化 | JEP 383 | 优化外部内存访问 API |
Java 16 | 孵化特性 | JEP 389 | 引入了外部链接器 API |
Java 16 | 第三次孵化 | JEP 393 | 功能优化 |
Java 17 | 孵化特性 | JEP 412 | 引入了外部函数和内存 API |
引入一个API
,它提供对本机代码的静态类型的纯Java访问。此API
与Foreign-Memory API
(JEP 393)一起,将大大简化绑定到本机库的错误处理过程。
13、JEP 414:向量 API(第二次孵化)
这是一个孵化功能。
Vector API 由JEP 338 提出并作为孵化 API 集成到 Java 16 中。
Vector API 旨在通过提供一种在 Java 中编写复杂向量算法的方法来提高向量计算的可预测和健壮性。许多领域都可以从这个显式向量 API 中受益,包括机器学习、线性代数、密码学、金融和 JDK 本身的代码。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 16 | 孵化特性 | JEP 338 | 提供一个平台无关的方式来表达向量计算,能够充分利用现代处理器上的向量硬件指令 |
Java 17 | 第二次孵化 | JEP 414 | 改进 |
14、JEP 415: 上下文特定的反序列化过滤器
15、移除的APIs、工具、容器
参考: