Java半年就会更新一次新特性,再不掌握就要落伍了:Java15 的新特性

网友投稿 710 2022-05-30

你好,我是看山。

本文收录在 《从小工到专家的 Java 进阶之旅》 系列专栏中。

从 2017 年开始,Java 版本更新策略从原来的每两年一个新版本,改为每六个月一个新版本,以快速验证新特性,推动 Java 的发展。从 《JVM Ecosystem Report 2021》 中可以看出,目前开发环境中有近半的环境使用 Java8,有近半的人转移到了 Java11,随着 Java17 的发布,相信比例会有所变化。

因此,准备出一个系列,配合示例讲解,阐述各个版本的新特性。

概述

Java15 是在 2020 年 9 月发布的一个短期版本,新增特性如下:

JEP 339:Edwards-Curve 数字签名算法

JEP 360:密封的类和接口(预览功能)

JEP 371:隐藏类

JEP 372:移除 Nashorn JavaScript 引擎

JEP 373:重新实现 DatagramSocket 接口

JEP 374:禁用偏向锁

JEP 375:instanceof 匹配模式(第二版预览功能)

JEP 377:ZGC:可伸缩低延迟垃圾收集器

JEP 378:文本块

JEP 379:Shenandoah:低暂停时间垃圾收集器

JEP 381:移除 Solaris 和 SPARC 端口 API

JEP 383:外部存储器访问 API(第二版孵化功能)

JEP 384:Record 类型(第二版预览功能)

JEP 385:废除 RMI Activation

接下来我们一起看看这些特性。

Edwards-Curve 数字签名算法(JEP 339)

Edwards-Curve 数字签名算法(EdDSA),一种根据 RFC 8032 规范所描述的 Edwards-Curve 数字签名算法(EdDSA)实现加密签名。

EdDSA 是一种现代的椭圆曲线方案,与 JDK 中的现有签名方案相比,EdDSA 具有更高的安全性和性能,因此备受关注。它已经在 OpenSSL 和 BoringSSL 等加密库中得到支持,目前在区块链领域用的比较多。

我们看下官方给的例子:

byte[] msg = "Hello, World!".getBytes(StandardCharsets.UTF_8); // example: generate a key pair and sign KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519"); KeyPair kp = kpg.generateKeyPair(); // algorithm is pure Ed25519 Signature sig = Signature.getInstance("Ed25519"); sig.initSign(kp.getPrivate()); sig.update(msg); System.out.println(Hex.encodeHexString(sig.sign())); // example: use KeyFactory to contruct a public key KeyFactory kf = KeyFactory.getInstance("EdDSA"); NamedParameterSpec paramSpec = new NamedParameterSpec("Ed25519"); EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdECPoint(true, new BigInteger("1"))); PublicKey pubKey = kf.generatePublic(pubSpec); System.out.println(pubKey.getAlgorithm()); System.out.println(Hex.encodeHexString(pubKey.getEncoded())); System.out.println(pubKey.getFormat());

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

例子中 Ed25519 是使用 SHA-512(SHA-2)和 Curve25519 的 EdDSA 签名方案。旨在提供与高质量 128 位对称密码相当的抗攻击能力,公钥长度为 256 位,签名长度为 512 位。

隐藏类(JEP 371)

Java15 引入了一个新的特性:隐藏类(Hidden Classes),一个专为框架而设计的特性。大多数开发人员不会直接使用这个特性,一般是通过动态字节码或 JVM 语言来使用隐藏类。

隐藏类有下面三个特点:

不可发现:在运行时生成内部类对象;

访问控制:只能通过反射访问,不能直接被其他字节码访问;

较短的生命周期:可独立于其他类加载、卸载,且效率很高,能够减少框架的内存占用。

隐藏类的功能特性还是比较有意思的,会涉及类加载、卸载、不可见、反射等很多内容,后续会开文单独聊,文章会放在 从小工到专家的 Java 进阶之旅 专栏中。

重新实现 DatagramSocket 接口(JEP 373)

老的 DatagramSocket API 在 Java15 中被重写,是继 Java14 重写 Socket API 的后续不走。这个特性是 Loom 项目的先决条件。

目前,DatagramSocket和MulticastSocket将所有的套接字委托为java.net.DatagramSocketImpl的实现,根据不同的平台,Unix 平台使用PlainDatagramSocketImpl,Windows 平台使用TwoStackPlainDatagramSocketImpl和DualPlainDatagramSocketImpl。抽象类DatagramSocketImpl是 Java1.1 提供的,功能很少且有一些过时方法,阻碍了 NOI 的实现。

类似于 Java14 中对 Socket API 的重写(参见 Java14 新特性),会在DatagramSocket内部封装一个DatagramSocket实例,将所有调用直接委托给该实例。包装实例或者使用 NIO 的DatagramChannel::socket创建套接字,或者是使用原始DatagramSocket类的实现DatagramSocketImpl实现功能(用于实现向后兼容)。

我们可以看下新的依赖图:

禁用偏向锁(JEP 374)

在 Java15 中,默认禁用偏向锁,弃用了所有相关命令行选项。

偏向锁是 HotSpot 中一种用于减少非竞争锁定开销的优化技术,不过在如今的应用程序中,优化增益不太明显了。

根据官方说法,使用偏向锁增益最多的是大量使用早期同步组件(比如Hashtable、Vector等),随着新的 API 实现和针对多线程场景引入的支持并发的数据结构,偏向锁的锁定及撤销,会带来性能的开销,从而是优化收益降低。

而且随着越来越多的功能特性引入,偏向锁在同步子系统中引入的大量代码,侵入 HotSpot 其他组件,带来代码的复杂性和维护成本,成为代码优化的阻碍。所以官方要将其移除。

不过,有些应用在禁用偏向锁后会出现性能下降,可以使用-XX:+UseBiasedLocking手动开启。

ZGC:可伸缩低延迟垃圾收集器(JEP 377)

ZGC 是在 Java11 引入的(参见 Java11 新特性),一直处于试验阶段,想要体验,需要在参数中使用-XX:+UnlockExperimentalVMOptions -XX:+UseZGC组合启用,在 Java15 中,ZGC 成为正式特性,想要使用可以直接用命令-XX:+UseZGC就行。

ZGC 是一个重新设计的并发的垃圾回收器,可以极大的提升 GC 的性能,支持任意堆大小而保持稳定的低延迟。从 https://openjdk.java.net/jeps/333 给出的数据可以看出来,在 128G 堆大小的测试中,ZGC 优势明显,找了一张网上的图片:

虽然 ZGC 愿景很好,但是还有很长的路要走,所以默认的垃圾收集器还是 G1。

Shenandoah:低暂停时间垃圾收集器(JEP 379)

Shenandoah 是在 Java12 引入的(参见)Java12 的新特性,本次和 ZGC 一起转正。同样的,想要使用 Shenandoah,不再需要参数-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC组合,只使用-XX:+UseShenandoahGC即可。需要注意的是,Shenandoah 只在 OpenJDK 中提供,OracleJDK 中并不包含。

文本块(JEP 378)

文本块是千呼万唤终于转正,在 Java13 中首次引入(参见 Java13 的新特性),在 Java14 中又增加了预览特性(参见 Java14 的新特性),终于在 Java15 确定下来,可以放心使用了。

我们再复习一下:

@Test void testTextBlock() { final String singleLine = "你好,我是看山,公众号「看山的小屋」。这行没有换行,而且我的后面多了一个空格 \n 这次换行了"; final String textBlockSingleLine = """ 你好,我是看山,公众号「看山的小屋」。\ 这行没有换行,而且我的后面多了一个空格、s 这次换行了"""; Assertions.assertEquals(singleLine, textBlockSingleLine); }

1

2

3

4

5

6

7

8

9

10

这个功能特性是代码可读性的优化。

预览

密封类和接口(JEP 360)

目前,Java 没有提供对继承的细粒度控制,只有 public、protected、private、包内控制四种非常粗粒度的控制方式。

为此,密封类的目标是允许单个类声明哪些类型可以用作其子类型。这也适用于接口,并确定哪些类型可以实现它们。该功能特性新增了sealed和non-sealed修饰符和permits关键字。

我们可以做如下定义:

public sealed class Person permits Student, Worker, Teacher {} public sealed class Student extends Person permits Pupil, JuniorSchoolStudent, HighSchoolStudent, CollegeStudent, GraduateStudent {} public final class Pupil extends Student {} public non-sealed class Worker extends Person {} public class OtherClass extends Worker {} public final class Teacher extends Person {}

1

2

3

4

5

6

7

8

9

10

11

12

我们可以先定义一个sealed修饰的类Person,使用permits指定被继承的子类,这些子类必须是使用final或sealed或non-sealed修饰的类。其中Student是使用sealed修饰,所以也需要使用permits指定被继承的子类。Worker类使用non-sealed修饰,成为普通类,其他类都可以继承它。Teacher使用final修饰,不可再被继承。

从类图上看没有太多区别:

但是从功能特性上,起到了很好的约束作用,我们可以放心大胆的定义可以公开使用,但又不想被非特定类继承的类了。

instanceof 模式匹配-预览第二版(JEP 375)

instanceof 模式匹配首先在 Java14 中提供预览功能(参见 Java14 特性),可以提供instanceof更加简洁高效的实现,在 Java15 中没有新增特性,主要是为了再次收集反馈,根据结果看,大家还是很期待这个功能,在 Java16 中正式提供。

我们再简单看下instanceof的改进:

@Test void test1() { final Object obj1 = "Hello, World!"; int result = 0; if (obj1 instanceof String str) { result = str.length(); } else if (obj1 instanceof Number num) { result = num.intValue(); } Assertions.assertEquals(13, result); }

1

2

3

4

5

6

7

8

9

10

11

12

Record 类型-预览第二版(JEP 384)

Record 类型用来增强 Java 语言特性,充当不可变数据载体。在 Java14 中提供预览功能(参见 Java14 新特性),在 Java15 中提供第二次预览,这次预览的目标是收集用户反馈。

比如,我们定义一个Person类:

public record Person(String name, String address) { }

1

2

我们转换为之前的定义会是一坨下面这种代码:

public final class PersonBefore14 { private final String name; private final String address; public PersonBefore14(String name, String address) { this.name = name; this.address = address; } public String name() { return name; } public String address() { return address; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PersonBefore14 that = (PersonBefore14) o; return Objects.equals(name, that.name) && Objects.equals(address, that.address); } @Override public int hashCode() { return Objects.hash(name, address); } @Override public String toString() { return "PersonBefore14{" + "name='" + name + '\'' + ", address='" + address + '\'' + '}'; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

Java 每半年就会更新一次新特性,再不掌握就要落伍了:Java15 的新特性

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

Record 类型特性有四个特性:

设计一个面向对象的结构,表达简单的信息聚合;

帮助开发人员专注于建模不可变数据,而不是可扩展的行为;

自动实现数据驱动的方法,比如equals、getter、setter等方法;

保留长期存在的 Java 规范,比如迁移兼容性。

我们不能将 Record 类型简单的理解为去除“样板化”代码的功能,它不是解决 JavaBean 命名约定的中很多模板化方法的冗余繁杂问题,它的目标不是类似 Lombok 等工具自动生成代码的功能,是从开发人员专注模型的角度出发的。

孵化

外部存储器访问 API-孵化第二版(JEP 383)

外部存储器访问 API 在 Java14 开始孵化(参见 Java14 新特性),在 Java15 中继续孵化状态,这个版本中增加了几个特性:

新的VarHandleAPI,用于定制内存访问句柄;

支持Spliterator接口实现并行处理内存段;

增强了对映射内存段的支持;

能够像本机调用一样操作或间接操作内存地址。

外部内存通常是说那些独立 JVM 之外的内存区域,可以不受 JVM 垃圾收集的影响,通常能够处理较大的内存。

这些新的 API 虽然不会直接影响多数的应用类开发人员,但是他们可以在内存的第三方库中提供支持,包括分布式缓存、非结构化文档存储、大型字节缓冲区、内存映射文件等。

其他

移除 Nashorn JavaScript 引擎(JEP 372)

Nashorn JavaScript 引擎最初在 Java8 中引入(参见 Java8 新特性),在 Java11 被标记为过期,在 Java15 中被删除,包括 Nashorn JavaScript 引擎、API、jjs 工具等内容。

Nashorn JavaScript 引擎是一个 JavaScript 脚本引擎,用来取代 Rhino 脚本引擎,对 ECMAScript-262 5.1 有完整的支持,增强了 Java 和 JavaScript 的兼容性,而且有很强的性能。

随着 GraalVM 和其他虚拟机技术最近的引入,Nashorn 引擎不再在 JDK 生态系统中占有一席之地。而且,ECMAScript 脚本语言结构、API 改变速度太快,Nashorn JavaScript 引擎维护成本太高,所以,直接删了。

移除 Solaris 和 SPARC 端口 API(JEP 381)

Solaris 和 SPARC 都已被 Linux 操作系统和英特尔处理器取代。放弃对 Solaris 和 SPARC 端口的支持将使 OpenJDK 社区的贡献者能够加速开发新功能,从而推动平台向前发展。

Solaris 和 SPARC 端口 API 在 Java14 中标记过时,在 Java15 中彻底移除。仅仅半年就痛下杀手,可见社区对于维护这些 API 深受折磨。

废除 RMI Activation(JEP 385)

RMI Activation 在 Java15 中标记为废除,会在未来版本删除。之所以被删除,是因为在现代的 web 应用中,已经不需要这种激活机制,继续维护,增加了 Java 开发人员的维护负担。在 Java8 的时候,已经将其设置为非必选项。

从开发系统的角度看,虽然 RMI Activation 是一个还不错的设计,但是已经有其他替代方案,继续维护开发下去,成本收益完全不匹配,及早舍弃,可以选择更加优秀的方案。有些类似于零边际成本的思想。

文末总结

本文介绍了 Java15 新增的特性,完整的特性清单可以从 https://openjdk.java.net/projects/jdk/15/ 查看。后续内容会发布在 从小工到专家的 Java 进阶之旅 系列专栏中。

青山不改,绿水长流,我们下次见。

推荐阅读

一文掌握 Java8 Stream 中 Collectors 的 24 个操作

一文掌握 Java8 的 Optional 的 6 种操作

使用 Lambda 表达式实现超强的排序功能

Java8 的时间库(1):介绍 Java8 中的时间类及常用 API

Java8 的时间库(2):Date 与 LocalDate 或 LocalDateTime 互相转换

Java8 的时间库(3):开始使用 Java8 中的时间类

Java8 的时间库(4):检查日期字符串是否合法

Java8 的新特性

Java9 的新特性

Java10 的新特性

Java11 中基于嵌套关系的访问控制优化

Java11 的新特性

Java12 的新特性

Java13 的新特性

Java14 的新特性

从小工到专家的 Java 进阶之旅

你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请、、关注。我还整理了一些精品学习资料,关注公众号「看山的小屋」,回复“资料”即可获得。

个人主页:https://www.howardliu.cn

个人博文:Java 每半年就会更新一次新特性,再不掌握就要落伍了:Java15 的新特性

CSDN 主页:https://kanshan.blog.csdn.net/

CSDN 博文:Java 每半年就会更新一次新特性,再不掌握就要落伍了:Java15 的新特性

Java JVM

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:怎样用Excel截屏让数据得到实时更新
下一篇:校招HTTP面试常见问题汇集(详解)
相关文章