目录
3、JEP 305:模式匹配的 instanceof(预览特性)
5、JEP 358:改进 NullPointerExceptions 提示信息
10、JEP 362: 弃用Solaris 和 SPARC的移植
13、JEP 365:Windows 上的 ZGC(实验特性)
14、JEP 366:弃用 ParallelScavenge + SerialOld GC 组合
16、JEP 370: 外部存储器 API (Incubator)
概述
JDK 14 于 2020 年 3 月 17 日正式发布。
JEP(Java Enhancement Proposal)Java增强提案
CSR(Compatibility & Specification Review) 兼容性和规范审查
变动说明
官网:
Java Platform, Standard Edition Java Language Updates, Release 14
更多参考:
JDK 14 Documentation - Home 更多版本:Java Platform, Standard Edition Documentation - Releases
Java Platform, Standard Edition Oracle JDK Migration Guide, Release 14
重要变更和信息
JDK 14 包含 16 个 新特性 ,分别为:
-
JEP 305: Pattern Matching for instanceof (Preview): instanceof 的模式匹配 (预览)
-
JEP 343: Packaging Tool (Incubator):打包工具 (Incubator)
-
JEP 345: NUMA-Aware Memory Allocation for G1:G1的 NUMA内存分配优化
-
JEP 349: JFR Event Streaming:JFR 事件流
-
JEP 352: Non-Volatile Mapped Byte Buffers:非原子性的 字节缓冲区 映射
-
JEP 358: Helpful NullPointerExceptions:友好的 空指针异常
-
JEP 359: Records (Preview):Record类
-
JEP 361: Switch Expressions (Standard):Switch表达式 (标准)
-
JEP 362: Deprecate the Solaris and SPARC Ports:弃用Solaris 和 SPARC的移植
-
JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector:移除CMS(Concurrent Mark Sweep)垃圾收集器
-
JEP 364: ZGC on macOS:macOS系统上的 ZGC
-
JEP 365: ZGC on Windows:Windows系统上的 ZGC
-
JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination:弃用 ParallelScavenge + SerialOld GC组合
-
JEP 367: Remove the Pack200 Tools and API:移除 Pack200 Tools和 API
-
JEP 368: Text Blocks (Second Preview):文本块 (第二个预览版)
-
JEP 370: oreign-Memory Access API (Incubator):外部存储器 API (孵化特性)
而其中与开发过程中直接相关的特性包括:JEP 305(instanceof
模式匹配)、JEP 361(Switch
表达式)、JEP 368(文本块)等。
下载地址
您可以从这个链接下载生产就绪的OpenJDK
版本。文件为压缩包,解压并设置环境变量就可以使用。
当然你也可以从这个链接下载Oracle JDK
版本(但是需要注意商用限制),更多版本下载。
Java14新特性总结
1、JEP 361:Switch 表达式(正式特性)
JEP 361 (tools/javac
)
这仍然是一个预览特性。
功能进化
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个版本的新特性转为正式特性 |
Switch 表达式是在 Java 12 中首次作为预览特性引入,而在 Java 13 中对 Switch 表达式做了增强改进:在块中引入了 yield
语句来返回值,而不是使用 break
。在Java 14
中成为一个标准特性。
Switch 表达式主要功能包括:
-
简化的语法:
switch
表达式使用更简洁的箭头语法 (->
)编写,可以直接返回一个值,且不再需要break
语句。 -
多值匹配:每个
case
分支可以同时匹配多个值,使用逗号分隔。 -
yield
关键字:当使用switch
表达式处理复杂逻辑时,可以根据情况使用yield
在代码中返回一个值。
示例代码:
// 旧写法:冗长,切容易出错。漏掉break会穿透到下一条件。 public static String getTypeOfDay0(String name) { String desp; switch (name) { case "稻": desp = "dào,俗称水稻、大米"; break; case "黍": desp = "shǔ,俗称黄米"; break; case "稷": desp = "jì,又称粟,俗称小米"; break; // 可以注释掉看看 case "麦": desp = "mài,俗称小麦"; break; case "菽": case "豆": desp = "shū,俗称大豆"; break; default: throw new IllegalStateException("不是五谷之一: " + name); } return desp; } // java12写法 public static String getTypeOfDay1(String name) { return switch (name) { case "稻" -> "俗称水稻、大米"; case "黍" -> "shǔ,俗称黄米"; case "稷" -> "jì,又称粟,俗称小米"; case "麦" -> "俗称小麦"; case "菽", "豆" -> "shū,俗称大豆"; default -> { throw new IllegalStateException("不是五谷之一: " + name); } }; } // java12写法:条件中需要特殊处理的情况,需要在外部单独定义一个变量接收处理值 public static String getTypeOfDay2_1(String name) { // 如果不需要特殊处理,可以直接返回 String desp; switch (name) { case "稻" -> desp = "俗称水稻、大米"; case "黍" -> desp = "shǔ,俗称黄米"; case "稷" -> desp = "jì,又称粟,俗称小米"; case "麦" -> desp = "俗称小麦"; case "菽", "豆" -> desp = "shū,俗称大豆"; default -> { // 处理复杂逻辑 if (name == null || name.isEmpty()) { desp = "名称为空"; } else { throw new IllegalStateException("不是五谷之一: " + name); } } } return desp; } // java13写法,即java14写法 public static String getTypeOfDay2(String name) { return switch (name) { case "稻" -> "俗称水稻、大米"; case "黍" -> "shǔ,俗称黄米"; case "稷" -> "jì,又称粟,俗称小米"; case "麦" -> "俗称小麦"; case "菽", "豆" -> "shū,俗称大豆"; default -> { // 处理复杂逻辑 if (name == null || name.isEmpty()) { yield "名称为空"; } else { throw new IllegalStateException("不是五谷之一: " + name); } } }; } @Test(priority = 0) // 不指定顺序时默认按字母顺序执行 public void test() { String name = "稷"; System.out.printf("%s:%s%n", name, getTypeOfDay0(name)); System.out.printf("%s:%s%n", name, getTypeOfDay1(name)); System.out.printf("%s:%s%n", name, getTypeOfDay2(name)); }
2、JEP 368:增强文本块(第二次预览)
JEP 368 (tools/javac
)
这是一个预览特性。在JDK 15
中成为正式特性。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 13 | 预览特性 | JEP 355 | 引入文本块,支持多行字符串定义 |
Java 14 | 预览特性 | JEP 368 | 改进文本块,增加了2个转义字符:\ (不换行)和\s (空格)。 |
将文本块添加到Java语言中。文本块是一个多行字符串文字,它避免了大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。这是JDK13中的预览语言特性。
我们以前从外部copy一段文本串到Java中,会被自动转义,如有一段以下字符串:
<html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html>
将其复制到Java的字符串中,会展示成以下内容:
"<html>\n" " <body>\n" " <p>天地玄黄,宇宙洪荒</p>\n" " </body>\n" "</html>\n";
即被自动进行了转义,这样的字符串看起来不是很直观,在JDK 13中,可以使用文本块定义。文本块是通过三个双引号("""
)来定义的,这标志着字符串的开始和结束。在这两个标志之间的所有文本,包括换行符和空格,都将被包含在字符串中。
示例代码:
@Test public void test() { // 旧写法 String html = "<html>\n" + // " <body>\n" + // " <p>天地玄黄,宇宙洪荒</p>\n" + // " </body>\n" + // "</html>"; System.out.println(html); // 新写法。 String html1 = """ <html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html> """; System.out.println(html1); }
执行结果:
<html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html> <html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html>
2者输出结果是一样的。但是显然新写法写起来也简单,也更易读。
SQL语句也可以使用:
String query = """ SELECT `id`, `name` FROM `users` WHERE `sex` = 1 ORDER BY `age`, `create_time`; """;
文本块可以用来定义一段格式化文本,而不需要想原来每一行都需要增加换行处理。
使用文本块好处:
-
多行字符串的简化:在之前的java中编写多行字符串时,只能需要通过使用
\n
来实现换行,通过+
来连接多个字符串。文本块则只需要将文本通过三个双引号("""
)包裹起来。 -
格式化和缩进处理:采用字符串拼接的方式,是无法格式化和缩进处理的,而使用文本块则会自动处理字符串的格式化和缩进。
-
无需处理特殊字符:在之前的java中对于特殊字符需要进行转义处理,但使用文本块后,则不需要。
文本块接受另外两个转义序列,分别是:\
和 \s
:(参见《程序员文本块指南》)。
-
\
(终止换行符):这个转义符用于去除行尾换行符,使当前文本的下一行不换行,而是直接拼在当前文本后面。 -
\s
(空格标识符):这个转义符表示这是一个空格,在它前面的空格也会保留,如果没有这个标识,前面的空格不会保留。
示例代码:
@Test public void test() { // 注意里面三山,文本比较长,不方便阅读。但是如果直接换行会影响最后的输出。这里需要一个标识符,标识一下不换行,换行只为了方便阅读。这就是\的作用。 // String text = """ 三山五岳,汉语成语,泛指华夏大地各名山。 三山,有三种说法:(1)是指华夏远古神话传说中的三条龙脉:喜马拉雅山脉(盘古开天辟地、共工怒触不周山)、昆仑山脉(玉帝居庭玉京山、嫦娥奔月)、天山山脉(西王母娘居庭、女娲炼石补天);\ (2)是道教传说中的三座仙山:蓬莱(蓬壶)、方丈山(方壶)、瀛洲(瀛壶);\ (3)今人所喜欢的三座旅游名山:黄山、庐山、雁荡山。 五岳指东岳泰山、西岳华山、南岳衡山、北岳恒山、中岳嵩山。 三山五岳遍布华夏大地,是中华民族的摇篮,是华夏祖先最早定居的地方,对中华民族的历史文化发展与研究有着重要的意义。 """; System.out.println(text); // 这里面五岳后面分别跟了0、1、2、3、4个空格,但是直接输出最后的空格会被忽略掉。 String wuyue1 = """ 东岳泰山 西岳华山 南岳衡山 北岳恒山 中岳嵩山 """; System.out.println(wuyue1); // 这里面五岳后面分别跟了0、1、2、3、4个空格,但是直接输出最后的空格会被忽略掉。用\s就不会忽略。注意\s本身标识空格,所以实际上最终输出的空格比前面说的多1个。 String wuyue2 = """ 东岳泰山\s 西岳华山 \s 南岳衡山 \s 北岳恒山 \s 中岳嵩山 \s """; System.out.println(wuyue2); }
执行结果:
三山五岳,汉语成语,泛指华夏大地各名山。 三山,有三种说法:(1)是指华夏远古神话传说中的三条龙脉:喜马拉雅山脉(盘古开天辟地、共工怒触不周山)、昆仑山脉(玉帝居庭玉京山、嫦娥奔月)、天山山脉(西王母娘居庭、女娲炼石补天);(2)是道教传说中的三座仙山:蓬莱(蓬壶)、方丈山(方壶)、瀛洲(瀛壶);(3)今人所喜欢的三座旅游名山:黄山、庐山、雁荡山。 五岳指东岳泰山、西岳华山、南岳衡山、北岳恒山、中岳嵩山。 三山五岳遍布华夏大地,是中华民族的摇篮,是华夏祖先最早定居的地方,对中华民族的历史文化发展与研究有着重要的意义。 东岳泰山 西岳华山 南岳衡山 北岳恒山 中岳嵩山 东岳泰山 [这是人为添加的一个符号为了显示空格的位置] 西岳华山 [这是人为添加的一个符号为了显示空格的位置] 南岳衡山 [这是人为添加的一个符号为了显示空格的位置] 北岳恒山 [这是人为添加的一个符号为了显示空格的位置] 中岳嵩山 [这是人为添加的一个符号为了显示空格的位置]
3、JEP 305:模式匹配的 instanceof(预览特性)
这是一个预览特性,在JDK 16
中成为正式特性。
在Java 14
之前,instanceof
主要用来检查对象的类型,检查匹配后,还需要对其进行类型强转才能使用该类型的变量,这显得很多余也很业余,而且存在手滑转错类型的可能。
Java SE 14
为instanceof
操作符引入了模式匹配;如果instanceof
运算符的结果为true
,则判断对象将自动绑定到声明的变量上。
在Java 14
之前的代码写法:
if (obj instanceof String) { String s = (String) obj; // 业务逻辑 }
在 Java 14 中,使用模式匹配的 instanceof
,这可以被简化为:
if (obj instanceof String s) { // 直接使用 s }
如果 obj
是 String
类型的实例,s
就会被自动声明并初始化为 obj
强制转换后的值。这样就避免了单独的类型转换步骤,并使得代码更加简洁易读。
@Test public void test() { Object name = "初唐四杰"; // 旧写法 if (name instanceof String) { String s = (String) name; // 业务逻辑 System.out.println(s); } // 新写法 if (name instanceof String s) { // 直接使用 s System.out.println(s); } }
作用域
绑定变量的作用域一般是在instanceof
内,但是在适当的条件下也可以扩展到外部
@Test public void test() { Object name = "初唐四杰"; if (!(name instanceof Integer i)) { // 是为是!处理,不能直接使用 i,否则编译报错 // System.out.println(i); return; } // 经过前面的return处理,i一定存在。这里可以使用i,你可以尝试将前面的return语句注释掉,看看,这里编译就报错了。因为情况变的不确定了。 System.out.println(i); }
与运算符结合
// 这个语句可以运行,因为&&是短路运算符,前面满足条件才会走到后面 if (name instanceof Integer in && in > 10) { // 处理 }
下面的语句编译不通过
// 这个语句编译会报错,因为in不一定存在 if (name instanceof Integer || in > 10) { // 处理 }
4、JEP 359:Record类 (预览特性)
JEP 359 (core-libs/java.lang
)
这是一个预览特性。在JDK 16
中成为正式特性。
在JDK14
中,引入了一个新类java.lang.Record
。这是一种新的类型声明。Records
允许我们以一种简洁的方式定义一个类,我们只需要指定其数据内容。对于每个Record
类,Java
都会自动地为其成员变量生成 equals()
, hashCode()
, toString()
方法,以及所有字段的访问器方法(getter),为什么没有 setter
方法呢?因为Record
的实例是不可变的,它所有的字段都是 final
的,这就意味着一旦构造了一个Record
实例,其状态就不能更改了。
与枚举一样,记录也是类的受限形式。它非常适合作为“数据载体”,即包含不想更改的数据的类,以及只包含最基本的方法(如构造函数和访问器)的类。
与前面介绍的其他预览特性一样,这个预览特性也顺应了减少Java
冗余代码的趋势,能帮助开发者编写更精炼的代码。
一个示例类
定义一个长方形类
final class Rectangle implements Shape { final double length; final double width; public Rectangle(double length, double width) { this.length = length; this.width = width; } double length() { return length; } double width() { return width; } }
它具有以下特点:
-
所有字段都是
final
的 -
只包含构造器:
Rectangle(double length, double width)
和2个访问器方法:length()
和width()
您可以用record
表示此类:
record Rectangle(float length, float width) { }
一个record
由一个类名称(在本例中为Rectangle
)和一个record
属性列表(在本示例中为float length
和float width
)组成。
record
会自动生成以下内容:
-
为每个属性生成一个
private final
的字段 -
为每个属性生成一个与组件名相同的访问方法;在本例中,这些方法是
Rectangle::length()
和Rectangle::width()
-
一个公开的构造函数,参数包括所有属性。构造函数的参数与字段对应。
-
equals()
和hashCode()
方法的实现,如果两个record
类型相同并且属性值相等,那么它们是相等的 -
toString()
方法的实现,包括所有字段名和他们的值。
紧凑型构造函数
如果你想在record
自定义一个构造函数。那么注意,它与普通的类构造函数不同,record
的构造函数没有参数列表:这被称为紧凑型构造函数。
例如,下面的record``HelloWorld
有一个字段message
。它的自定义构造函数调用Objects.requireNonNull(message)
,如果message
字段是用null
值初始化的,则抛出NullPointerException
。(自定义记录构造函数仍然会初始化所有字段)
record HelloWorld(String message) { public HelloWorld { java.util.Objects.requireNonNull(message); } }
测试代码:
@Test public void test() { HelloWorld h1 = new HelloWorld(null); // new HelloWorld("天地玄黄宇宙洪荒"); //用这个测试,可以发现字段还是会初始化的 System.out.println(h1); }
这个测试代码执行报java.lang.NullPointerException
异常。
使用限制
以下是record
类使用的限制:
-
Record
类不能继承任何类 -
Record
类不能声明实例字段(与record
组件相对应的private final
字段除外);任何其他声明的字段都必须是静态的 -
Record
类不能是抽象的;它是final
的 -
Record
类的成员变量是final
的
除了这些限制之外,record
类的行为类似于常规类:
-
可以在类中声明
record
;嵌套record
是static
的 -
record
可以实现接口 -
使用
new
关键字实例化record
-
您可以在
record
的主体中声明静态方法、静态字段、静态初始值设定项、构造函数、实例方法和嵌套类型 -
可以对
record
和record
的属性进行注释
与record
相关的API
java.lang.Class
类有2个方法与record
相关:
-
RecordComponent[] 返回类型getRecordComponents(): 返回
record
的所有字段列表。 -
boolean isRecord(): 与
isEnum()
类似,如果是record
则返回true
。
5、JEP 358:改进 NullPointerExceptions 提示信息
JEP 358 (hotspot/runtime
)
java.net.Socket
和java.net.ServerSocket
的API
底层实现在此版本中已被替换。
默认使用新的API,但是旧的实现(称为PlainSocketImpl
或Plain
实现)并没有删除,可以继续运行。可以通过增加系统属性jdk.net.usePlainSocketImpl
或将其值设为true
来使用旧实现,即使用-Djdk.net.usePlasinSocketImpl
或-Djdk.net.usePlainSocketImpl=true
=来运行。该属性也可以在JDK
网络配置文件中进行配置,该文件位于${java.home}/conf/net.properties
中。旧实现以及用于选择旧实现的系统属性将在将来的版本中删除。
新的API使用全新实现的 NioSocketImpl
来替换原来的PlainSocketImpl
。可以查看java.net.SocketImpl
的源代码
import lombok.Data; @Data public class TestUser { private String name; private Integer age; private Like like; } @Data class Like { private String name; private Fee fee; } @Data class Fee { private Double money; }
测试方法:
@Test public void test() { TestUser user = new TestUser(); var name = user.getLike().getFee().getMoney(); System.out.println(name); }
在Java 14
之前,你可能会得到如下的错误:
Exception in thread "main" java.lang.NullPointerException at com.ld.mytest.test.java14.NullExceptionTest.test(NullExceptionTest.java:9)
不幸的是,这一行多个方法调用,任何一个都可能会返回null。根本无法判断是谁导致了NullPointerException,这在调试bug
的过程中非常的不方便。
而在Java 14
中,输出结果:
java.lang.NullPointerException: Cannot invoke "com.ld.mytest.test.java14.Like.getFee()" because the return value of "com.ld.mytest.test.java14.TestUser.getLike()" is null at com.ld.mytest.test.java14.NullExceptionTest.test(NullExceptionTest.java:9) ……
可以看出,提示信息明确提示了空指针的原因是getLike()
方法返回为空。
在Java 14
中诊断信息只有在使用下述标志运行Java时才有效:
-XX:+ShowCodeDetailsInExceptionMessages
示例如下:
java -XX:+ShowCodeDetailsInExceptionMessages NullExceptionTest
这个配置在Java 15
中已经设置为默认。
这项改进不仅对于方法调用有效,其他可能会导致NullPointerException
的地方也有效,包括字段访问、数组访问、赋值等。
6、JEP 343:打包工具(孵化特性)
在Java 14
之前,Java 应用通常依赖于 JAR 文件来分发和运行,或者需要第三方工具来创建本地应用程序包,Java 14
引入一个新的打包工具,基于 javapackager
的jpackage
,用于打包Java
应用程序为特定平台的本地安装包。
该特性提供一个官方的、简单易用的打包工具,使 Java 应用可以更容易地分发和安装在各种平台上,包括Windows
、macOS
和Linux
。同时该工具能够创建包含 Java
运行时环境(JRE
)的自包含应用程序包。这意味着用户不需要在其设备上预先安装Java
运行时环境。
jpackage
打包工具可以将Java
应用程序打包为针对特定平台的安装包,这个安装包包含所有必需的依赖项。该应用程序可以以普通JAR
文件集合或模块集合的方式提供。软件包格式可以分为:
-
Linux:
deb
和rpm
-
macOS:
pkg
和dmg
-
Windows:
msi
和exe
7、JEP 345:NUMA-Aware 内存分配
G1垃圾收集器现在尝试在新一代中跨垃圾收集分配和保持同一NUMA(Non-Uniform Memory Access)节点上的对象。这类似于并行GC NUMA意识。
G1试图使用严格的交织在所有可用的NUMA节点上均匀地分布Humongous和Old区域。从年轻一代复制到老一代的对象的放置是随机的。
这些新的NUMA感知内存分配启发法是通过使用-XX:+UseNUMA命令行选项自动启用的。
8、JEP 349:JFR Event Streaming
JDK Flight Recorder(JFR)现在支持对Java应用程序的连续监控,允许使用位于JDK.JFR.consumer包中的新API动态消耗事件。使用JFR时始终启用该功能,这意味着直到最后一秒的记录数据可用于进程内和进程外消耗。
9、JEP 352: 非原子性的字节缓冲区映射
10、JEP 362: 弃用Solaris 和 SPARC的移植
11、JEP 363: 删除 CMS 垃圾收集器
CMS(Concurrent Mark Sweep)垃圾收集器是一个以最小化应用程序停顿时间为目标的收集器,最初设计用于老年代的垃圾收集。
随着新一代垃圾收集器(如 G1、ZGC 和 Shenandoah)的出现和成熟,CMS 的技术相对过时,并且维护成本逐渐增高。此外,新的收集器在性能和功能上均优于 CMS。
Java 14 正式移除了 CMS 垃圾收集器及其相关代码。
12、JEP 364:macOS 上的 ZGC(实验特性)
该特性将 ZGC 的支持扩展到 macOS,这使得 macOS 上的 Java 开发者和用户也能够利用 ZGC 的高性能特性。
13、JEP 365:Windows 上的 ZGC(实验特性)
该特性将 ZGC 的支持扩展到 Windows,这使得 Windows 上的 Java 开发者和用户也能够利用 ZGC 的高性能特性。
14、JEP 366:弃用 ParallelScavenge + SerialOld GC 组合
随着 JVM 的发展,垃圾收集器技术也在不断进步。新的垃圾收集器(如 G1、ZGC 和 Shenandoah)为性能和效率提供了更好的解决方案。而 ParallelScavenge + SerialOld 这个组合,在年轻代中使用并行算法,而在老年代中使用串行算法,这种组合方式已经不具备优势了,由于这个组合很少使用,但是却要花费巨大工作量来进行维护,得不偿失,所以 Java 14 弃用该组合。
15、JEP 367:删除 Pack200 工具和 API
Pack200 是一种专门为 Java 类文件设计的压缩技术,最初在 Java 5 中引入。它可以显著减小 JAR 文件的大小,因此被广泛用于优化网络传输 Java 应用程序和库。
随着时间的推移和技术的发展,网络速度的提高和存储成本的降低减少了对极端压缩技术的需求。同时,新的打包技术(如 jlink 和 jmod)的出现,以及 Java 模块系统的引入,使得 Pack200 的应用场景变得有限。
Java 14 宣布正式移除 Pack200 工具和 API。
16、JEP 370: 外部存储器 API (Incubator)
17、移除的APIs、工具、容器
参考:
Oracle JDK和OpenJDK之间的差异
尽管官方已经声明了让OpenJDK
和Oracle JDK
二进制文件尽可能接近的目标,但至少对于JDK 14
来说,这两个选项之间仍然存在一些差异。
目前的差异是:
-
Oracle JDK
提供了安装程序(msi
、rpm
、deb
等),它们不仅将JDK
二进制文件放置在系统中,还包含更新规则,在某些情况下还可以处理一些常见的配置,如设置常见的环境变量(如Windows
中的JAVA_HOME
)和建立文件关联(如使用JAVA
启动.jar
文件)。OpenJDK
仅作为压缩档案(tar.gz
或.zip
)提供。 -
在版本号为
9
和10
下,javac —release
的行为会有所不同。Oracle JDK
的二进制文件中包含了一些没有加入到OpenJDK
二进制文件中的 API,比如javafx
、资源管理以及(在 JDK 11 变更之前)JFR APIs
。 -
Usage Logging
仅在Oracle JDK
中可用。 -
Oracle JDK
要求使用Java
加密扩展(JCE(Java Cryptography Extension ))代码签名证书对第三方加密提供程序进行签名。OpenJDK
继续允许使用未签名的第三方加密提供程序。 -
java -version
命令输出结果不同。Oracle JDK
将输出java
并包含LTS。Oracle生成的OpenJDK
将显示OpenJDK
,不包括Oracle
特定的LTS
标识符。 -
Oracle JDK
将在OTN
许可证下发布。任何许可证文件都需要指向OTN
。OpenJDK
将在GPLv2wCP
下发布,并将包括GPL
许可证。 -
Oracle JDK
将在FreeType
许可证下分发FreeType
,而OpenJDK
则将在GPLv2
下分发。因此,\legal\java.desktop\freetype.md
的内容将有所不同。 -
Oracle JDK
有Java cup
和steam
图标,而OpenJDK有Duke
图标。 -
Oracle JDK
源代码包括ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
的说明,OpenJDK源代码包括GPL许可条款。