目录
概述
变动说明
重要变更和信息
下载地址
Java16新特性总结
1、JEP 395:Record类(正式特性)
功能进化
一个示例类
紧凑型构造函数
使用限制
与record相关的API
2、JEP 394:模式匹配的 instanceof(正式特性)
功能进化
作用域
与运算符结合
3、JEP 397:密封的类和接口(第二次预览)
功能进化
密封类
什么是密封类
示例代码
4、细微改动
(1)增加Stream.toList()方法(JDK-8180352 )
Stream.toList()和stream.collect(Collectors.toList())的区别
(2)java.time包的格式化支持一天中的数据段(JDK-8180352 )
(3)HttpClient的默认实现返回可取消的Future对象(JDK-8245462)
(4)修正Path.of或Paths.get的第一个参数为null时不会抛出空指针异常的问题(JDK-8254876)
5、JEP 338:向量 API(孵化器特性)
6、JEP 347:启用 C++14 语言特性
7、JEP 357:将JDK的源代码库从Mercurial迁移到Git
8、JEP 369:将JDK的源代码库托管到GitHub
9、JEP 376:ZGC 并发线程处理
功能进化
10、JEP 380:Unix-Domain 套接字通道
11、JEP 386:AlpineLinux 移植
12、JEP 387:弹性元空间
13、JEP 388:Windows/AArch64 移植
14、JEP 389:外部函数与内存 API(孵化特性)
功能进化
15、JEP 390:对基于值的类发出警告
16、JEP 392:打包工具(正式特性)
功能进化
安装.Net Framework 3
安装WiX
17、JEP 393:外部存储器访问 API(第三次孵化)
功能进化
18、JEP 396:默认强封装 JDK 内部元素
19、移除的APIs、工具、容器
Oracle JDK和OpenJDK之间的差异
概述
JDK 16 于 2021 年 3 月 16 日正式发布。
JEP(Java Enhancement Proposal)Java增强提案
CSR(Compatibility & Specification Review) 兼容性和规范审查
变动说明
官网:
Java Platform, Standard Edition Java Language Updates, Release 16
JDK 16 Release Notes
JDK 16
更多参考:
JDK 16 Documentation - Home 更多版本:Java Platform, Standard Edition Documentation - Releases
Java Platform, Standard Edition Oracle JDK Migration Guide, Release 16
重要变更和信息
JDK 16 包含 17 个 新特性 ,分别为:
-
JEP 338: Vector API (Incubator) 向量 API(孵化特性)
-
JEP 347: Enable C++14 Language Features 启用 C++14 语言特性
-
JEP 357: Migrate from Mercurial to Git 将JDK的源代码库从Mercurial迁移到Git
-
JEP 369: Migrate to GitHub 将JDK的源代码库托管到GitHub
-
JEP 376: ZGC: Concurrent Thread-Stack Processing ZGC 并发线程处理
-
JEP 380: Unix-Domain Socket Channels Unix-Domain 套接字通道
-
JEP 386: Alpine Linux Port AlpineLinux 移植
-
JEP 387: Elastic Metaspace 弹性元空间
-
JEP 388: Windows/AArch64 Port Windows/AArch64 移植
-
JEP 389: Foreign Linker API (Incubator) 外部函数与内存 API(孵化特性)
-
JEP 390: Warnings for Value-Based Classes 对基于值的类发出警告
-
JEP 392: Packaging Tool 打包工具(正式版)
-
JEP 393: Foreign-Memory Access API (Third Incubator) 外部存储器访问 API(第三次孵化)
-
JEP 394: Pattern Matching for instanceof instanceof的模式匹配 (正式特性)
-
JEP 395: Records Record类(正式特性)
-
JEP 396: Strongly Encapsulate JDK Internals by Default 默认强封装 JDK 内部元素
-
JEP 397: Sealed Classes (Second Preview) 密封类(第二次预览)
而其中与开发过程中直接相关的特性主要包括:JEP 394(instanceof的模式匹配 (正式特性))、JEP 395(Record类)、JEP 397(密封类(第二次预览))等。
下载地址
您可以从这个链接下载生产就绪的OpenJDK
版本。文件为压缩包,解压并设置环境变量就可以使用。
当然你也可以从这个链接下载Oracle JDK
版本(但是需要注意商用限制),更多版本下载。
Java16新特性总结
1、JEP 395:Record类(正式特性)
JEP 395 (tools/javac
)
功能进化
Java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 预览特性 | JEP 359 | 引入Record类作为预览特性 |
Java 15 | 预览特性 | JEP 384 | 修正及优化,语法上同上一版没有区别 |
Java 16 | 正式特性 | JEP 395 | 成为正式特性 |
在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);}}
测试代码:
@Testpublic 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
。
2、JEP 394:模式匹配的 instanceof(正式特性)
JEP 394 (tools/javac
)
功能进化
Java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 预览特性 | JEP 305 | 引入instanceof 模式匹配为预览特性 |
Java 15 | 预览特性 | JEP 375 | 修正及优化,语法上同上一版没有区别 |
Java 16 | 正式特性 | JEP 394 | 成为正式特性 |
在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
强制转换后的值。这样就避免了单独的类型转换步骤,并使得代码更加简洁易读。
@Testpublic void test() {Object name = "初唐四杰";// 旧写法if (name instanceof String) {String s = (String) name;// 业务逻辑System.out.println(s);}// 新写法if (name instanceof String s) {// 直接使用 sSystem.out.println(s);} }
作用域
绑定变量的作用域一般是在instanceof
内,但是在适当的条件下也可以扩展到外部
@Testpublic 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) {// 处理}
3、JEP 397:密封的类和接口(第二次预览)
JEP 397
这仍是一个预览特性。
功能进化
Java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 15 | 预览特性 | JEP 360 | 引入了密封类作为预览特性。 |
Java 16 | 预览特性 | JEP 397 | 第二次预览 |
密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。
继承,作为面向对象语言的三大特性之一,我们工作过程中经常使用,可以重写父类的方法。
通常开发项目时,我们会先将接口提供出来,然后根据情况给出不同的基础实现类,子类再基础这些基础实现类进行扩展,我们可能并不希望子类直接继承接口,当然直接继承接口的写法从代码上看没有任何问题,但存在安全隐患。一般我们会通过开发约束对,这样的情况说一些要求,但是这样并不能杜绝这类问题。
密封类
为了进一步增强继承的限制能力,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{}
这样,子类就不能随便继承父类了。
core-libs/java.util.stream
4、细微改动
(1)增加Stream.toList()方法(JDK-8180352 )
java.util.Stream
中添加了一个新的方法toList
。可以就可以不使用stream.collect(Collectors.toList())
来转成List了。生成的是unmodifiableList
,不可修改。
@Testpublic void test() {// 出自《笠翁对韵》,与《声律启蒙》、和《训蒙骈句》合称吟诗作对三基。String str = "天对地,雨对风。大陆对长空。山花对海树,赤日对苍穹。雷隐隐,雾蒙蒙。日下对天中。风高秋月白,雨霁晚霞红。牛女二星河左右,参商两曜斗西东。十月塞边,飒飒寒霜惊戍旅;三冬江上,漫漫朔雪冷渔翁。"+ "河对汉,绿对红。雨伯对雷公。烟楼对雪洞,月殿对天宫。云叆叇,日曈朦。腊屐对渔蓬。过天星似箭,吐魄月如弓。驿旅客逢梅子雨,池亭人挹藕花风。茅店村前,皓月坠林鸡唱韵;板桥路上,青霜锁道马行踪。"+ "山对海,华对嵩。四岳对三公。宫花对禁柳,塞雁对江龙。清暑殿,广寒宫。拾翠对题红。庄周梦化蝶,吕望兆飞熊。北牖当风停夏扇,南檐曝日省冬烘。鹤舞楼头,玉笛弄残仙子月;凤翔台上,紫箫吹断美人风。";List<String> list = Arrays.asList(str.split("。"));List<String> result = list.stream().filter(s -> s.contains("对")).filter(e -> e.length() > 5).toList();System.out.printf("list 长度:%s 数据:%s%n", result.size(), result);}
执行结果
list 长度:6 数据:[天对地,雨对风, 山花对海树,赤日对苍穹, 河对汉,绿对红, 烟楼对雪洞,月殿对天宫, 山对海,华对嵩, 宫花对禁柳,塞雁对江龙]
Stream.toList()和stream.collect(Collectors.toList())的区别
Stream.toList()
返回是一个unmodifiableList
不可变的List,而使用Stream.collect(Collectors.toList())
返回的是一个普通的List,是可以做增删改操作的。
(2)java.time
包的格式化支持一天中的数据段(JDK-8180352 )
日期格式新增字母 B 表示一天中的时间段(day period)(转换规则按Unicode Locale Data Markup Language (LDML) Part 4: Dates中的规定),在类 java.time.format.DateTimeFormatter/DateTimeFormatterBuilder
中提供了支持,可以表示一天中的时间段,如"in the morning"(上午)或"at night"(晚上),而不仅仅是am/pm。
// 时间是 13:45,输出:下午,其他还有上午、晚上等DateTimeFormatter.ofPattern("B").format(LocalTime.now())
(3)HttpClient的默认实现返回可取消的Future对象(JDK-8245462)
默认的HttpClient
是通过调用HttpClient.newHttpClient()
或调用HttpClient.newBuilder()
返回的构建器再调用build
方法创建的。默认HttpClient
中sendAsync
方法的实现现在返回可取消的CompletableFuture
对象。对未完成的可取消未来调用cancel(true)
,会尝试取消HTTP请求,以尽快释放底层资源。可以参考HttpClient::sendAsync
方法的API文档。
上面创建HttpClient
的方法可能抛出UncheckedIOException异常(JDK-8248006)。
(4)修正Path.of
或Paths.get
的第一个参数为null时不会抛出空指针异常的问题(JDK-8254876)
之前的版本中,Path.of()
和Paths.get()
方法的参数有多个时,第一个参数并没有进行不能为null的检查。在这个版本中,如果第一个参数为null
则会跟其他参数为null一样抛出NullPointerException
。
5、JEP 338:向量 API(孵化器特性)
JEP 338 (hotspot/compiler
)
提供孵化模块jdk.incultor.vector
的初始迭代,以表示在运行时可靠地编译为支持的CPU架构上的最佳向量硬件指令的向量计算,从而实现优于等效标量计算的性能。
6、JEP 347:启用 C++14 语言特性
JEP 347
JDK 的某些组件,特别是 Java 虚拟机(JVM),是采用 C++ 语言开发的。随着 C++ 的不断演进,新标准的推出带来了诸多有益的特性,比如改进的类型推断、智能指针和 lambda 表达式等,这些特性对于提升代码品质和开发效率都有显著作用。然而,JDK 的开发过程中主要还停留在使用较为旧版 C++
标准(例如 C++98
),这限制了可利用的语言功能的广度。
随着 Java 16
对 C++14
的支持,这象征着 JDK 开发者在编写 JDK 源码时可以借助 C++14
的标准特性,从而有望提高 JDK 的开发效率和代码质量。
7、JEP 357:将JDK的源代码库从Mercurial迁移到Git
JEP 357
8、JEP 369:将JDK的源代码库托管到GitHub
JEP 369
9、JEP 376:ZGC 并发线程处理
JEP 376 (hotspot/gc
)
ZGC 是 Java 11 中引入的一个实验性垃圾收集器,设计目标是实现低延迟一个可伸缩的、低延迟的垃圾收集器。在 Java 11 到 Java 15 期间,ZGC 已经表现出了卓越的低延迟特性,但在某些情况下,它需要暂停应用线程来处理堆中的引用。
Java 16 引入并发线程处理,旨在将这些暂停转变为并发处理,以进一步降低延迟。该特性的核心是将 ZGC 中的最后一块堆管理工作 — 引用处理(Reference Processing) — 转变为并发执行。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 11 | 预览特性 | JEP 333 | 引入 ZGC 作为实验特性 |
Java 15 | 正式特性 | JEP 377 | 成为正式特性 |
Java 16 | 正式特性 | JEP 376 | 并发线程处理 |
10、JEP 380:Unix-Domain 套接字通道
JEP 380 (core-libs/java.nio
)
在java.nio.channels
、SocketChannel
和ServerSocketChannel
类中提供对Unix
域套接字(AF_Unix
)的支持。
11、JEP 386:AlpineLinux 移植
JEP 386
12、JEP 387:弹性元空间
JEP 387 (hotspot/runtime
)
在 Java 8 中,元空间替代了原有的永久代(PermGen),成为存储类元数据的区域。虽然这一改变解决了永久代大小限制的问题,但元空间的内存管理依然不够高效,特别是在类卸载后释放内存方面。Java 16 引入弹性元空间,目的是进一步提高元空间的内存使用效率,特别是在动态加载和卸载类的应用场景中。
弹性元空间彻底检修了VM内部元空间和类空间的实现。用于类元数据的内存较少。节省的效果在涉及大量小粒度类加载器的场景中最为明显。类卸载后,内存会及时返回到操作系统。
添加了一个开关来微调元空间回收:-XX:MetaspaceReclaimPolicy=(balanced|aggressive|none)
,默认是balanced
,使虚拟机回收内存,同时将计算开销保持在最低限度;激进地适度提高回收率,代价是记账成本略高;没有一个完全关闭回收。
开关InitialBootClassLoaderMetaspaceSize
和UseLargePagesInMetaspace
已被弃用。
13、JEP 388:Windows/AArch64 移植
JEP 388
将JDK移植到Windows/AArch64。
14、JEP 389:外部函数与内存 API(孵化特性)
JEP 389 (core-libs
)
孵化阶段。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 孵化特性 | JEP 370 | 引入了外部内存访问 API作为孵化特性 |
Java 15 | 第二次孵化 | JEP 383 | 优化外部内存访问 API |
Java 16 | 孵化特性 | JEP 389 | 引入了外部链接器 API |
Java 16 | 第三次孵化 | JEP 393 | 功能优化 |
引入一个API,它提供对本机代码的静态类型的纯Java访问。此API与Foreign-Memory API
(JEP 393)一起,将大大简化绑定到本机库的错误处理过程。
15、JEP 390:对基于值的类发出警告
JEP 390 (core-libs
)
Java 16
对@jdk.internal.ValueBased
注解加入了基于值的类的告警。
将基础类的包装类指定为基于值的,弃用其构造函数以表示降落会删除(Java 9
注解@Deprecated
得到了增强,增加了 since 和 forRemoval 两个属性,可以分别指定一个程序元素被废弃的版本,以及是否会在今后的版本中被删除。),从而提示新的弃用警告。Java平台中任何基于值的类的实例上进行Synchronized
操作,编译时将产生警告。
Java 16
引入该特性的主要目的是警告开发者关于基于值的类的使用。基于值的类是一种特殊类型的类,它们的实例是不可变的。应该当成,单纯的数据载体,不应该被视为类的实例。
当我们尝试进行同步(使用synchronized
关键字)时,编译器会发出警告,比如:
public void test1(Integer count) {for (int i = 0; i < 10; i++) {new Thread(() -> {synchronized (count) { // 这里会产生编译告警}}).start();}}
该特性通过告警的方式,引导开发者更好地理解和利用基于值的类,为未来 Java 版本中可能的优化和特性变化做好准备。
警告信息:
Integer is a value-based type which is a discouraged argument for the synchronized statement
16、JEP 392:打包工具(正式特性)
JEP 392 (tools/jpackage
)
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 孵化特性 | JEP 343 | 引入了打包工具孵化特性 |
Java 16 | 正式特性 | JEP 392 | 成为正式特性 |
在Java 14
之前,Java 应用一般使用JAR
包的形式进行分发和运行,或者需要第三方工具来创建本地应用程序包,Java 14
引入一个新的打包工具:基于 javapackager
的jpackage
,用于打包Java
应用程序为特定平台的本地安装包。但是在Java 14
中它是作为一个孵化特性引入的,在Java 16
中作为正式特性发布。
前置条件
事先准备
-
JAVA 16
版本以上 -
Windows
系统下使用jpackage
指令需要用到WiX
,版本3.0
及以上,并配置好PATH
变量; -
java
程序提前打包成jar
文件
如果没有安装WiX
,使用jpackage
打包时会提示:
安装.Net Framework 3
WiX
依赖于.Net Framework 3
,所以如果之前没有安装,那么在装Wix
之前需要先安装.Net Framework 3
。
安装步骤: 1、控制面板的“程序和功能”中点击“启用和关闭Windows功能” 2、勾选.NET Framework 3.5(包括.NET 2.0 和 3.0)
,点击确定,再点击同意下载 3、等待自动下载并安装完成 4、最后重新启动计算机
安装WiX
下载地址:WiX v3 | WiX Toolset
下载完成后,点击exe文件,选择Install。等待安装完成。
出现Complete即完成。
默认安装位置:C:\Program Files (x86)\WiX Toolset v3.14
配置环境变量:在Path
中增加C:\Program Files (x86)\WiX Toolset v3.14\bin
。
1、生成运行程序:jpackage --type app-image --input [Jar包所在文件夹] --runtime-image [Jre文件夹] --name [应用名称] --main-jar [可执行Jar包] --icon [程序图标的路径] --app-version [版本号] --vendor [程序供应商的名称] --copyright [版权信息] --description [应用描述] --dest [输出目录]2、生成安装程序:jpackage --type msi --win-dir-chooser --name [安装程序的名称] --app-image [运行程序的文件夹] --dest [输出目录]
"F:\Program Files\Java\jdk-21.0.2\bin\jlink" --add-modules java.base,java.desktop --output myjre"F:\Program Files\Java\jdk-21.0.2\bin\jpackage" --name myapp --runtime-image myjre --input . --main-jar mytest-pkg-1.0.0-SNAPSHOT.jar"F:\Program Files\Java\jdk-21.0.2\bin\jpackage" --name myapp --runtime-image myjre --input . --main-jar mytest-pkg-1.0.0-SNAPSHOT.jar \--win-dir-chooser --win-menu --win-per-user-install --win-shortcut --win-shortcut-prompt
jpackage -h 输出帮助信息
用法:jpackage <options>示例用法:--------------生成适合主机系统的应用程序包:对于模块化应用程序:jpackage -n name -p modulePath -m moduleName/className对于非模块化应用程序:jpackage -i inputDir -n name \--main-class className --main-jar myJar.jar从预构建的应用程序映像:jpackage -n name --app-image appImageDir生成应用程序映像:对于模块化应用程序:jpackage --type app-image -n name -p modulePath \-m moduleName/className对于非模块化应用程序:jpackage --type app-image -i inputDir -n name \--main-class className --main-jar myJar.jar要为 jlink 提供您自己的选项,请单独运行 jlink:jlink --output appRuntimeImage -p modulePath \--add-modules moduleName \--no-header-files [<additional jlink options>...]jpackage --type app-image -n name \-m moduleName/className --runtime-image appRuntimeImage生成 Java 运行时程序包:jpackage -n name --runtime-image <runtime-image>一般选项:@<filename>从文件读取选项和/或模式可以多次使用此选项。--type -t <type>要创建的程序包的类型有效值为:{"app-image", "exe", "msi"}如果未指定此选项,则将创建与平台相关的默认类型。--app-version <version>应用程序和/或程序包的版本--copyright <copyright string>应用程序的版权--description <description string>应用程序的说明--help -h将用法文本输出到输出流并退出,用法文本中包含适用于当前平台的每个有效选项的列表和说明--icon <file path>应用程序包图标的路径(绝对路径或相对于当前目录的路径)--name -n <name>应用程序和/或程序包的名称--dest -d <destination path>用来放置所生成的输出文件的路径(绝对路径或相对于当前目录的路径)默认为当前的工作目录。--temp <directory path>用来创建临时文件的新目录或空白目录的路径(绝对路径或相对于当前目录的路径)如果指定,则在任务完成时将不删除临时目录,必须手动删除临时目录。如果未指定,则将创建一个临时目录,并在任务完成时删除该临时目录。--vendor <vendor string>应用程序的供应商--verbose启用详细的输出--version将产品版本输出到输出流并退出。用来创建运行时映像的选项:--add-modules <模块名称>[,<模块名称>...]要添加的模块的逗号 (",") 分隔列表此模块列表连同主模块(如果指定)将作为 --add-module 参数传递到 jlink。如果未指定,则仅使用主模块(如果指定了 --module),或者使用默认的模块集(如果指定了--main-jar)。可以多次使用此选项。--module-path -p <module path>...路径的 ; 分隔列表每个路径要么是模块的目录,要么是模块化 jar 的路径。(每个路径可以是绝对路径,也可以是相对于当前目录的路径。)可以多次使用此选项。--jlink-options <jlink 选项>要传递给 jlink 的选项列表(用空格分隔)如果未指定,则默认为 "--strip-native-commands--strip-debug --no-man-pages --no-header-files"。可以多次使用此选项。--runtime-image <directory path>将复制到应用程序映像的预定义运行时映像的路径(绝对路径或相对于当前目录的路径)如果未指定 --runtime-image,jpackage 将运行 jlink 以使用如下选项创建运行时映像:--strip-debug、--no-header-files、--no-man-pages 和--strip-native-commands。用来创建应用程序映像的选项:--input -i <directory path>包含要打包的文件的输入目录的路径(绝对路径或相对于当前目录的路径)输入目录中的所有文件将打包到应用程序映像中。--app-content <additional content>[,<additional content>...]要添加到应用程序有效负载中的文件和/或目录的逗号分隔路径列表。此选项可以多次使用。用来创建应用程序启动程序的选项:--add-launcher <launcher name>=<file path>启动程序的名称和包含关键字-值对列表的属性文件的路径(绝对路径或相对于当前目录的路径)可以使用关键字 "module"、"main-jar"、"main-class"、"description"、"arguments"、"java-options"、"app-version"、"icon"、"launcher-as-service"、"win-console"、"win-shortcut"、"win-menu"、"linux-app-category" 和 "linux-shortcut"。这些选项将添加到原始命令行选项中或者用来覆盖原始命令行选项,以构建额外的替代启动程序。将从命令行选项构建主应用程序启动程序。可以使用此选项构建额外的替代启动程序,可以多次使用此选项来构建多个额外的启动程序。--arguments <main class arguments>在没有为启动程序提供命令行参数时,要传递到主类的命令行参数可以多次使用此选项。--java-options <java options>要传递到 Java 运行时的选项可以多次使用此选项。--main-class <class name>要执行的应用程序主类的限定名称只有在指定了 --main-jar 时才能使用此选项。--main-jar <main jar file>应用程序的主 JAR;包含主类(指定为相对于输入路径的路径)可以指定 --module 或 --main-jar 选项,但是不能同时指定两者。--module -m <module name>[/<main class>]应用程序的主模块(以及可选的主类)此模块必须位于模块路径中。如果指定了此选项,则将在 Java 运行时映像中链接主模块。可以指定 --module 或 --main-jar 选项,但是不能同时指定这两个选项。用来创建应用程序启动程序的与平台相关的选项:--win-console为应用程序创建控制台启动程序,应当为需要控制台交互的应用程序指定用来创建应用程序包的选项:--about-url <url>应用程序主页的 URL--app-image <directory path>用来构建可安装程序包的预定义应用程序映像的位置(绝对路径或相对于当前目录的路径)--file-associations <file path>包含关键字-值对列表的属性文件的路径(绝对路径或相对于当前目录的路径)可以使用关键字 "extension"、"mime-type"、"icon" 和 "description"来描述此关联。可以多次使用此选项。--install-dir <directory path>默认安装位置下面的相对子路径--license-file <file path>许可证文件的路径(绝对路径或相对于当前目录的路径)--resource-dir <directory path>覆盖 jpackage 资源的路径可以通过向该目录中添加替代资源来覆盖 jpackage 的图标、模板文件和其他资源。(绝对路径或相对于当前目录的路径)--runtime-image <directory path>要安装的预定义运行时映像的路径(绝对路径或相对于当前目录的路径)在创建运行时程序包时需要使用选项。--launcher-as-service请求创建安装程序,以将主应用程序启动程序注册为后台服务类型应用程序。用来创建应用程序包的与平台相关的选项:--win-dir-chooser添加一个对话框以允许用户选择产品的安装目录。--win-help-url <url>用户可以从中获取更多信息或技术支持的 URL--win-menu请求为此应用程序添加开始菜单快捷方式--win-menu-group <menu group name>此应用程序所在的开始菜单组--win-per-user-install请求基于每个用户执行安装--win-shortcut请求为此应用程序添加桌面快捷方式--win-shortcut-prompt添加一个对话框以允许用户选择是否将由安装程序创建快捷方式。--win-update-url <url>可用应用程序更新信息的 URL--win-upgrade-uuid <id string>与此程序包的升级相关联的 UUID
17、JEP 393:外部存储器访问 API(第三次孵化)
JEP 393 (core-libs
)
孵化阶段。
功能进化
java版本 | 特性类型 | JEP | 特性 |
---|---|---|---|
Java 14 | 孵化特性 | JEP 370 | 引入了外部内存访问 API作为孵化特性 |
Java 15 | 第二次孵化 | JEP 383 | 优化外部内存访问 API |
Java 16 | 孵化特性 | JEP 389 | 引入了外部链接器 API |
Java 16 | 第三次孵化 | JEP 393 | 功能优化 |
18、JEP 396:默认强封装 JDK 内部元素
JEP 396 (core-libs
)
在 Java 16 之前,许多 JDK 内部的类和成员尽管没有正式成为公共 API,但仍然可以被外部代码所访问。从 Java 16 开始,默认情况下,这些内部 API 被强制封装,阻止了对它们的非法访问,但关键的内部API(如sun.misc.Unsafe
)除外。防止开发者依赖于非稳定的、未经官方支持的内部实现,提高了代码的长期稳定性。
启动器选项的默认值--illegal-access 现在是deny
而不是permit
。这可能造成,那些使用了JDK内部的类、方法或字段的大多数代码将无法运行。临时可以通过指定--illegal-access=permit
在JDK16上运行。但是,该选项将在将来的版本中删除 。
Java 16 明确了哪些是公共 API,哪些是 JDK 内部使用的 API,帮助开发者避免依赖于可能在未来版本中改变的内部实现。鼓励开发者使用稳定且官方支持的公共 API,而不是依赖于内部的、可能随时变更的实现。
19、移除的APIs、工具、容器
参考:
-
Java SE 16中移除的API
-
JDK 16中移除的特性和容器
Oracle JDK和OpenJDK之间的差异
尽管官方已经声明了让OpenJDK
和Oracle JDK
二进制文件尽可能接近的目标,但至少对于JDK 16
来说,这两者之间仍然存在一些差异。
目前的差异是:
-
Oracle JDK
提供了安装程序(msi
、rpm
、deb
等),它们不仅将JDK
二进制文件放置在系统中,还包含更新规则,在某些情况下还可以处理一些常见的配置,如设置常见的环境变量(如Windows
中的JAVA_HOME
)和建立文件关联(如使用JAVA
启动.jar
文件)。OpenJDK
仅作为压缩档案(tar.gz
或.zip
)提供。 -
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. 使用受许可条款约束
的说明(OTN(Oracle Technology Network License Agreement for Oracle Java SE )协议),OpenJDK源代码包含GPL
协议。