概述
JDK 14 于 2020 年 3 月 17 日正式发布。
JEP(Java Enhancement Proposal)Java增强提案
CSR(Compatibility & Specification Review) 兼容性和规范审查
变动说明
官网:
更多参考:
docs.oracle.com/en/java/jav… 更多版本:docs.oracle.com/en/java/jav…
重要变更和信息
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
在代码中返回一个值。
示例代码:
typescript
复制代码
// 旧写法:冗长,切容易出错。漏掉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中,会被自动转义,如有一段以下字符串:
css
复制代码
<html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html>
将其复制到Java的字符串中,会展示成以下内容:
swift
复制代码
"<html>\n" " <body>\n" " <p>天地玄黄,宇宙洪荒</p>\n" " </body>\n" "</html>\n";
即被自动进行了转义,这样的字符串看起来不是很直观,在JDK 13中,可以使用文本块定义。文本块是通过三个双引号("""
)来定义的,这标志着字符串的开始和结束。在这两个标志之间的所有文本,包括换行符和空格,都将被包含在字符串中。
示例代码:
less
复制代码
@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); }
执行结果:
css
复制代码
<html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html> <html> <body> <p>天地玄黄,宇宙洪荒</p> </body> </html>
2者输出结果是一样的。但是显然新写法写起来也简单,也更易读。
SQL语句也可以使用:
ini
复制代码
String query = """ SELECT `id`, `name` FROM `users` WHERE `sex` = 1 ORDER BY `age`, `create_time`; """;
文本块可以用来定义一段格式化文本,而不需要想原来每一行都需要增加换行处理。
使用文本块好处:
- 多行字符串的简化:在之前的java中编写多行字符串时,只能需要通过使用
\n
来实现换行,通过+
来连接多个字符串。文本块则只需要将文本通过三个双引号("""
)包裹起来。 - 格式化和缩进处理:采用字符串拼接的方式,是无法格式化和缩进处理的,而使用文本块则会自动处理字符串的格式化和缩进。
- 无需处理特殊字符:在之前的java中对于特殊字符需要进行转义处理,但使用文本块后,则不需要。
文本块接受另外两个转义序列,分别是:`` 和 \s
:(参见《程序员文本块指南》)。
- `` (终止换行符) :这个转义符用于去除行尾换行符,使当前文本的下一行不换行,而是直接拼在当前文本后面。
\s
(空格标识符) :这个转义符表示这是一个空格,在它前面的空格也会保留,如果没有这个标识,前面的空格不会保留。
示例代码:
ini
复制代码
@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); }
执行结果:
scss
复制代码
三山五岳,汉语成语,泛指华夏大地各名山。 三山,有三种说法:(1)是指华夏远古神话传说中的三条龙脉:喜马拉雅山脉(盘古开天辟地、共工怒触不周山)、昆仑山脉(玉帝居庭玉京山、嫦娥奔月)、天山山脉(西王母娘居庭、女娲炼石补天);(2)是道教传说中的三座仙山:蓬莱(蓬壶)、方丈山(方壶)、瀛洲(瀛壶);(3)今人所喜欢的三座旅游名山:黄山、庐山、雁荡山。 五岳指东岳泰山、西岳华山、南岳衡山、北岳恒山、中岳嵩山。 三山五岳遍布华夏大地,是中华民族的摇篮,是华夏祖先最早定居的地方,对中华民族的历史文化发展与研究有着重要的意义。 东岳泰山 西岳华山 南岳衡山 北岳恒山 中岳嵩山 东岳泰山 [这是人为添加的一个符号为了显示空格的位置] 西岳华山 [这是人为添加的一个符号为了显示空格的位置] 南岳衡山 [这是人为添加的一个符号为了显示空格的位置] 北岳恒山 [这是人为添加的一个符号为了显示空格的位置] 中岳嵩山 [这是人为添加的一个符号为了显示空格的位置]
3、JEP 305:模式匹配的 instanceof(预览特性)
这是一个预览特性,在JDK 16
中成为正式特性。
在Java 14
之前,instanceof
主要用来检查对象的类型,检查匹配后,还需要对其进行类型强转才能使用该类型的变量,这显得很多余也很业余,而且存在手滑转错类型的可能。
Java SE 14
为instanceof
操作符引入了模式匹配;如果instanceof
运算符的结果为true
,则判断对象将自动绑定到声明的变量上。
在Java 14
之前的代码写法:
javascript
复制代码
if (obj instanceof String) { String s = (String) obj; // 业务逻辑 }
在 Java 14 中,使用模式匹配的 instanceof
,这可以被简化为:
javascript
复制代码
if (obj instanceof String s) { // 直接使用 s }
如果 obj
是 String
类型的实例,s
就会被自动声明并初始化为 obj
强制转换后的值。这样就避免了单独的类型转换步骤,并使得代码更加简洁易读。
typescript
复制代码
@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
内,但是在适当的条件下也可以扩展到外部
typescript
复制代码
@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); }
与运算符结合
java
复制代码
// 这个语句可以运行,因为&&是短路运算符,前面满足条件才会走到后面 if (name instanceof Integer in && in > 10) { // 处理 }
下面的语句编译不通过
java
复制代码
// 这个语句编译会报错,因为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
冗余代码的趋势,能帮助开发者编写更精炼的代码。
一个示例类
定义一个长方形类
arduino
复制代码
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
表示此类:
arduino
复制代码
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
。(自定义记录构造函数仍然会初始化所有字段)
arduino
复制代码
record HelloWorld(String message) { public HelloWorld { java.util.Objects.requireNonNull(message); } }
测试代码:
csharp
复制代码
@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
的源代码
kotlin
复制代码
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; }
测试方法:
scss
复制代码
@Test public void test() { TestUser user = new TestUser(); var name = user.getLike().getFee().getMoney(); System.out.println(name); }
在Java 14
之前,你可能会得到如下的错误:
php
复制代码
Exception in thread "main" java.lang.NullPointerException at com.ld.mytest.test.java14.NullExceptionTest.test(NullExceptionTest.java:9)
不幸的是,这一行多个方法调用,任何一个都可能会返回null。根本无法判断是谁导致了NullPointerException,这在调试bug
的过程中非常的不方便。
而在Java 14
中,输出结果:
kotlin
复制代码
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时才有效:
ruby
复制代码
-XX:+ShowCodeDetailsInExceptionMessages
示例如下:
ruby
复制代码
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许可条款。