查看原文
其他

Java 14 新特性速览

ImportNew 2021-12-02

(给ImportNew加星标,提高Java技能)

来自作者投稿  作者:覃佑桦

www.techgeeknext.com/java/java14-features

Java14发布了!


2020年3月17日,世界上使用最多的编程语言和应用开发平台JavaSE 14及JDK14发布了。Java14发布了大量JEP(Java增强建议)。新版包含的JEP数量甚至比Java12和13的总和还要多。

Java14主要加入了16个新特性(JEP),包括最新的Java API、JDK Fight Recorder监控等等。完整的新特性列表如下:

1. instanceof模式匹配(预览特性)
2. 非易失性映射字节缓冲(孵化器)
3. 实用的NullPointerException
4. 新switch表达式(标准特性)
5. 打包工具(孵化器)
6. G1中的NUMA内存分配优化
7. JFR事件流
8. Records(预览特性)
9. 弃用Solaris和SPARC端口
10. 移除CMS垃圾回收器
11. 针对macOS的ZGC(实验特性)
12. 针对Windows的ZGC(实验特性)
13. 弃用ParallelScavenge+SerialOld GC组合
14. 移除Pack200工具和API
15. 文本块(第二次预览版)
16. 外存访问API(孵化器)

Oracle为所有开发者和企业用户开放了Java14下载。

1. instanceof模式匹配

instanceof操作符用来检查对象引用是否为指定的Type实例,检查的结果使用boolean返回。Java14对instanceof操作符进行了改进,加入了模式匹配。改进后的instanceof让实现逻辑变得清晰,不用在条件判断后再为对象强制类型转换。

Java14以前
if (obj instanceof String) {
String str = (String) obj; // 需要再次声明和对象转换
.. str.contains(..)..
}else{
str = ....
}

Java14
if (!(obj instanceof String str)) {
.. str.contains(..)..// 不必再声明str对象进行强制类型转换
} else {
.. str....
}

更多示例:
if (obj instanceof String str && str.length() > 5) {.. str.contains(..)..}

if (obj instanceof String str || str.length() > 5) {.. str.contains(..)..}


注意:只有object不为null时instanceof才会匹配并把结果分配给str。使用instanceof模式匹配可以减少Java程序中进行强制转换。

2. 非易失性映射字节缓冲(孵化器)

简要地说,JDK1.4开始Java NIO File API就出现了。

FileChannel MappedByteBuffer 该API将部分文件数据加载到虚拟内存中,然后引入了Path特性。Path是一个接口。使用Java NIO开发时,可以用Path替代java.io.File表示文件或目录。 

现在,Java 14对MappedByteBuffer进行了改进,将部分文件数据加载到非易失性存储器(NVM)中。NVM非易失性存储是指类似ROM(只读存储器)、闪存、硬盘等存储器,即使关闭电源数据也不会丢失。易失性存储器比如RAM,如果关闭电源则无法保存数据。API唯一的变化是加入了一个新枚举供FileChannel客户端使用。表示请求映射位于NVM支持的文件系统而非传统文件系统。

风险与假设

该特性允许通过ByteBuffer将NVM作为堆外资源进行管理。与此相关的增强功能JDK-8153111正在研究将NVM用于堆数据。可能还会考虑使用NVM存储JVM元数据。不同的NVM管理模式一起使用时也许不兼容,也可能仅仅是不合适。API建议只支持最大2GB映射空间。必要时可以改动实现策略,使其符合,JDK-8180628以突破此限制。

3. 实用的NullPointerException

Java14对JVM生成的NullPointerException异常信息进行了改进。程序提前终止时,新特性将为开发者和技术支持人员提供有用的信息。由于NPE几乎可以出现在程序中的任何位置,尝试捕获它们并从中恢复通常不太可行。开发人员只能靠JVM确认NPE实际的发生时间。例如,假设下面代码抛出一个NPE:
a.i = 99;


JVM会输出导致NPE的方法、文件名和行号:
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)


现在假设下面代码抛出一个NPE:
a.b.c.i = 99;

仅通过文件名和行号并不能精确指出究竟哪个变量为null。是变量a、变量b还是变量c?

JDK14 JEP改进了异常信息,按照下面的方式抛出该异常,能够确切知道哪个变量为null。
Exception in thread "main" java.lang.NullPointerException:
Cannot read field 'c' because 'a.b' is null.
at Prog.main(Prog.java:5)


与此同时也带来一些风险。null详细信息可能包含源代码中的变量名。暴露这些信息可能会带来安全风险。

4. 新switch表达式(标准特性)

Java14扩展了switch语句的功能,可以把switch作为表达式使用。支持箭头(->)操作符生成或返回值。该特性在JDK12和JDK13中是预览功能。

例1Java14以前
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}

Java14

switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}

例2Java14以前   

int numLetters;
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
numLetters = 6;
break;
case TUESDAY:
numLetters = 7;
break;
case THURSDAY:
case SATURDAY:
numLetters = 8;
break;
case WEDNESDAY:
numLetters = 9;
break;
default:
throw new IllegalStateException("Wat: " + day);
}


Java14
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
};

更多示例   
// 箭头标签
static void grade(int g) {
System.out.println(
switch (g) {
case 1 -> "A";
case 2 -> "B";
default -> "C";
}
);
}
------------------------------------------
// 生成值-引入新的yield用法
int j = switch (day) {
case MONDAY -> 0;
case TUESDAY -> 1;
default -> {
int d = day.toString().length();
int result = f(d);
yield result;
}
};


5. 打包工具(孵化器)

该特性是一种能够简化安装过流程的打包功能,能解决应用所需的各种依赖项。有时仅提供一个JAR文件是不够的,还需要提供原生安装包。打包工具还可以作为其它技术的补充。

jpackage工具把Java应用打包成平台特定格式的包,其中包含应用所有的依赖项。即一组普通JAR文件或模块的集合。支持的包格式:

1. Linux:deb和rpm
2. macOS:pkg和dmg
3. Windows:MSI和EXE

6. G1中的NUMA内存分配优化

非一致性内存访问(NUMA)是一种将微处理器集群配置为多处理系统的方式,因此可以在本地共享内存、提高性能并扩展系统能力。Java14实现了NUMA内存分配优化,提升G1在大型计算机上表现。G1中的堆是一组固定大小区域。虽然指定-XX:+UseLargePages选项可以使用大页面,多个区域可以组成一个物理页面,但是一个区域通常是一组物理页面。如果指定+XX:+UseNUMA选项,初始化JVM时上述将把区域平均分布在所有可用NUMA节点上。

7. JFR事件流

Java14提供了一个新的API,JDK Flight Recorder(JFR)可以通过它持续监视进程内与进程外部应用程序。

使用非Stream方式记录相同的事件集,开销可能甚至小于1%。事件流将与非Stream方式同时执行。

jdk.jfr.consumer包位于 jdk.jfr模块中,扩展了异步订阅事件的功能。

8.Record(预览特性)

这是JDK14中一个预览功能。使用record精简类声明代码。

定义一个数据类需要编写很多低效重复的模板代码:构造函数、accessor、equals()、hashCode()、toString()等。Java计划使用record精简这些重复代码。

示例:
Java14以前:   
final class Point {
public final int x;
public final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

// equals、hashCode、toString方法
// 其它什么也没干


Java14使用record   
record Point(int x, int y) { }


使用record的局限:

· record不能继承其它类,除了描述状态的私有final字段外不能声明其它字段实例。

· record默认为final且不能声明为abstract。这意味着record API完全由状态定定义,不能被其它类或者其它record修改。

· record组件隐式定义为final。

9. 弃用Solaris和SPARC端口

· Java14弃用了Solaris/SPARC、Solaris/x64和Linux/SPARC端口,未来可能将它们移除。

· 取消对这些端口的支持,能让OpenJDK社区的贡献者加速开发新功能,推动平台向前发展。

构建配置变化下面尝试配置Solaris/SPARC   
$ bash ./configure
...
checking compilation type... native
configure: error: The Solaris and SPARC ports are deprecated and may be removed in a future release.\
Use --enable-deprecated-ports=yes to suppress this error.
configure exiting with result code 1
$

设置--enable-deprecated-port=yes构建选项可以解决错误继续配置。   
$ bash ./configure --enable-deprecated-ports=yes
...
checking compilation type... native
configure: WARNING: The Solaris and SPARC ports are deprecated and may be removed in a future release.
...
Build performance summary:
* Cores to use: 32
* Memory limit: 96601 MB


构建Solaris和SPARC版本会报告错误或警告,包括Solaris/SPARC、Solaris/x64、Linux/SPARC。

10. 移除CMS垃圾回收器

Java14删除了CMS垃圾收集器。

不仅停用CMS编译,从源代码中删除了gc/cms目录中的内容,而且删除了仅限CMS的选项。

使用-XX:+UseConcMarkSweepGC选项启用CMS时会提示以下警告:   
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; \
support was removed in <version>

虚拟机会继续使用默认垃圾收集器。

11. JEP 364:针对macOS的ZGC(实验特性)

JEP 364实际上和JEP3 65一样。JEP 364针对MacOS提供了ZGC垃圾收集器。它将ZGC垃圾收集器移植到了macOS。正如JEP351中的描述,该JEP的功能还包括使用收集器释放未使用的设备内存。自Java13开始就支持此功能。ZGC的macOS实现包含两个部分:

1. 在macOS上支持多重映射(multi-mapping)内存。

2. ZGC支持不连续预留内存。

12. JEP 365:针对Windows的ZGC(实验特性)

JEP 365实际上和JEP 364一样。JEP 365针对Windows提供了ZGC垃圾收集器支持。

大多数ZGC代码都与平台无关,不需要为Windows修改。由于早期版本没有预留内存必须的API,因此不支持Windows 10和Windows Server 1803之前的版本。

ZGC的Windows实现进行了以下工作:

1. 支持多重映射内存

由于ZGC使用着色指针(colored pointer),因此需要多重映射支持,以便可以从进程地址空间中的多个位置访问相同的物理内存。Windows内存使用分页文件为物理内存提供了一个标识(句柄),与映射的虚拟内存地址无关。ZGC使用该标识可以将相同的物理内存映射到多个地址。

2. 支持将内存映射到预留地址空间,基于分页文件技术。

Windows内存管理API没有POSIX mmap/munmap灵活,当涉及到将文件备份内存映射到之前预留的空间时尤其如此。因此,ZGC使用了Windows地址占位符概念。在Windows 10 V1803和Windows Server中引入了占位符概念。ZGC不支持Windows其它早期版本。

3. 支持堆内存任意区域映射与取消映射操作。

ZGC堆的布局要求支持任意粒度的内存映射和取消映射,以及堆页面动态调整大小与重新调整。此要求与Windows地址空间占位符结合使用时,需要特别注意,因为占位符必须由程序显式拆分/合并,而不是由操作系统自动拆分/合并(与Linux一样)。

4. 支持堆内存任意区域提交和撤销提交操作。

ZGC能在Java程序运行时动态提交和撤销物理内存。为了支持这些操作,物理内存被分为多个分页文件片段。每个片段都对应一个ZGC堆单元,可以独立提交和撤销。

13. JEP 366:弃用ParallelScavenge+SerialOld GC组合

JEP366包含垃圾收集器,它的目标是弃用Parallel Scavenge和Serial Old垃圾收集算法的组合。除了弃用-XX:+UseParallelGC-XX :- UseParallelOldGC组合之外,-XX:UseParallelOld GC选项也被弃用,因为它的作用是取消老年代并行GC,支持老年代串行GC。因此,任何与UseUseParallelOldGC选项有关的用法都会输出警告。

14. JEP 367:移除Pack200工具和API

Pack 200是JavaSE 5.0中JSR 200实现的JAR文件压缩方案。

Java14从java.util.jar包中移除了pack200和unpack200工具以及pack200 API。这些工具和API在JavaSE 11中已废弃,会在随后的版本中移除。该JEP最终会从JDK主版本中移除3种类型。即之前标记 @Deprecated(forRemoval = true) 注解的基础模块:

· java.util.jar.Pack200
· java.util.jar.Pack200.Packer
· java.util.jar.Pack200.Unpacker

15. JEP 368:文本块(第二次预览版)

在Java中,想要把HTML、XML、SQL或JSON代码片段嵌入到代码中通常难以阅读和保留。并且为了克服此问题,Java14 引入了文本块(Text Block)。

文本块包含零个或多个字符,这些字符由开始分隔符和结束分隔符包围。

没有使用文本块HTML示例   
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";

文本块的开始分隔符以三个双引号(""")开始,可以接零个或多个空格,最后是一个换行符。开始分隔符换行后面接着是文本块的内容。

结束分隔符同样是三个双引号。文本块内容在三个双引号之前结束。

与String不同,文本块中可以直接使用双引号。当然也可以使用\" 但不是必需的。选择宽分隔符(""")的好处是可以直接使用"不需要转义,而且能从视觉上把文本块与String进行区分。

文本块是多行String文本。使用文本块能避免大多数转义的情况,支持自动格式化字符串,并且在必要时开发者能自行格式化字符串。

使用文本块的HTML示例   
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";


2019年初,JEP 355提议把文本块功能作为JEP 326(原始字符串字)的后续改进,随后该提议被撤回。

2019年中,JDK13把文本块作为预览功能加入,接着Java14加入了两个新的转义符。

其一,用\表示换行。其二,是用/s表示一个空格。

换行符示例:
// 不使用文本块
String literal = "two escape sequences first is for newlines " +
"and, second is to signify white space " +
"or single space.";

// 使用 \ 看起来像这样:
String text = """
two escape sequences first is for newlines \
and, second is to signify white space \
or single space.\
"""
;


空白或单空格示例:   
// 在每行的结尾使用 \s,保证每行的长度为6个字节
String colors = """
aaa\s
bbb\s
ccc\s
""";


16. JEP 370:外存访问API(孵化器)

许多流行的Java库和程序都支持访问外部存储器,例如Ignite、MapDB、Memcached和Netty的ByteBuf API。这样可以避免垃圾回收(比如维护大型缓存)、跨进程共享内存、通过将文件内存映射进行序列化和反序列化(例如mmap)带来的开销以及引入的不可预测性。然而,Java API没有提供适合的外存访问解决方案。

Java14通过JEP 370引入了高效的Java API,使得Java应用程序能够安全有效地访问Java堆外内存。外部存储API提出了三个重要的抽象:MemorySegment、MemoryAddress和MemoryLayout。
 

推荐阅读  点击标题可跳转

Java 14 新功能介绍!

用 Java 实现梯度下降

18 个 Java8 日期处理的实践,太有用了!


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

好文章,我在看❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存