写点什么

☕️从 Java8 到 Java17 的新特性(五):Java13 的新特性

作者:看山
  • 2022 年 5 月 17 日
  • 本文字数:3595 字

    阅读完需:约 12 分钟

☕️从 Java8 到 Java17 的新特性(五):Java13 的新特性

你好,我是看山。


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


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


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

概述

本文讲解一下 Java13 的特性,这个版本在语法特性上增加不多,值得关注的是两个预览功能:Switch 表达式和文本块,另外可以关乎的是性能优化方面的:动态类数据共享(CDS)存档、ZGC 动态释放未使用内存、Socket API 重构。这些方面可以看出,Java 的升级方向有两个,一是增加功能,增加新的语法特性;二是增强功能,提升已有功能性能。

预览功能

Java13 引入了两个新的语法特性:Switch 表达式和文本块。这些预览功能是为了让开发者尝鲜的同时,可以快速调整,反馈好就留下,不好就移除。目前来看,这些特性还是挺香的。


Switch 表达式

在 Java12 中 Switch 表达式首次以预览版的身份出现,在 Java13 中又做了增强,在 Java14 正式提供。Java13 添加了yield关键字,用来返回值。


yieldreturn的区别在于,yield只会跳出switch块,return是跳出当前方法或循环。


比如下面的例子,在 Java12 之前,要判断日期可以这样写:


@Testvoid testSwitch() {    final DayOfWeek day = DayOfWeek.from(LocalDate.now());    String typeOfDay = "";    switch (day) {        case MONDAY:        case TUESDAY:        case WEDNESDAY:        case THURSDAY:        case FRIDAY:            typeOfDay = "Working Day";            break;        case SATURDAY:        case SUNDAY:            typeOfDay = "Rest Day";            break;    }
Assertions.assertFalse(typeOfDay.isEmpty());}
复制代码


在 Java12 提供的 Switch 表达式预览功能,我们可以简化一下:


@Testvoid testSwitchExpression() {    final DayOfWeek day = DayOfWeek.SATURDAY;    final String typeOfDay = switch (day) {        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";        case SATURDAY, SUNDAY -> "Day Off";    };
Assertions.assertEquals("Day Off", typeOfDay);}
复制代码


这样可以实现判断,但是没有办法在表达式中实现其他逻辑了。于是 Java13 补齐了这个功能:


@Testvoid testSwitchExpression13() {    final DayOfWeek day = DayOfWeek.SATURDAY;    final String typeOfDay = switch (day) {        case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {            System.out.println("Working Day: " + day);            yield "Working Day";        }        case SATURDAY, SUNDAY -> {            System.out.println("Day Off: " + day);            yield "Day Off";        }    };
Assertions.assertEquals("Day Off", typeOfDay);}
复制代码


这里需要说明一下,既然是预览功能,会与正式提供功能有些出入。上面的代码是在 Java14 环境中编写,与 Java13 发布的功能描述有些差异,这点不必深究,已经废弃的约束就是不存在。

文本块

一直以来,Java 中的字符串定义都是以双引号括起来的形式,不支持多行书写,所以在需要多行字符串中,需要使用转义符表示,既不好看、还不好读,更不好维护。


千呼万唤始出来,终于有了文本块功能。


比如,我们想要写一段 Json 格式的数据,Java13 之前需要写成:


String json = "{\n"        + "  \"wechat\": \"hellokanshan\",\n"        + "  \"wechatName\": \"看山、",\n"        + "  \"mp\": \"kanshanshuo\",\n"        + "  \"mpName\": \"看山的小屋、"\n"        + "}\n";
复制代码


但是在 Java13 预览版中可以写作:


String json2 = """        {          "wechat": "hellokanshan",          "wechatName": "看山",          "mp": "kanshanshuo",          "mpName": "看山的小屋"        }        """;
复制代码


少了很多的+、换行、转移等字符,看着更加直观。


这个功能在 Java15 中正式提供。

动态类数据共享(CDS)存档

CDS 是 Java5 引入的一种类预处理方式,可以将一组类共享到一个归档文件中,借助内存映射加载类数据,减少启动时间,并可实现在多 JVM 之间共享的功能。在 Java10 对其进行扩展,增大了 CDS 使用范围,即 AppCDS(参见 Java10 新特性)。到了 Java12,将 CDS 归档文件作为了默认功能开放出来(参见 Java12 新特性)。


但是这个功能在使用的时候还是有些麻烦。为了生成归档文件,开发人员必须先对应用程序进行试运行,创建一个类列表,然后将其转储到归档文件中。然后,这个归档才可以用来在 JVM 之间共享元数据。


Java13 简化了这个过程:允许 Java 应用在运行结束后动态归档,即将已被加载但不属于 CDS 的类(包括自定义类和引用库的类)动态添加到 CDS 归档文件中。不用再提供归档类的列表,通过更加简洁的方式创建包含应用程序的归档。


我们可以使用-XX:ArchiveClassesAtExit参数控制应用程序退出时创建 CDS 归档文件:


java -XX:ArchiveClassesAtExit=<archive filename> -cp <app jar> AppName
复制代码


也可以使用-XX:SharedArchiveFile来使用动态存档功能:


java -XX:SharedArchiveFile=<archive filename> -cp <app jar> AppName
复制代码

ZGC 增强:释放未使用内存

ZGC 是 Java11 中引入的一个可伸缩、低延迟的垃圾收集器,主要目标包括:GC 停顿时间不超过 10ms;可以处理从几百 MB 的小堆,到几个 TB 的大堆;应用吞吐能力不会下降超过 15%等(参见 Java11 的新特性)。


但是 ZGC 并没有像 Hotspot 中的 G1 和 Shenandoah 那样,可以主动释放未使用的内存,对于多数应用程序来说,CPU 和内存都是稀缺资源,尤其是现在云上环境和虚拟化技术,如果应用程序占用的内存长期处于空闲状态,还紧握住不释放,就是极大的浪费。


在 Java13 中对其进行改进,包括:


  • 可释放空闲内存

  • 支持的最大堆大小从 4TB 扩大到 16TB


我们来看下 ZGC 的内部逻辑。


ZGC 堆由一组称为 ZPages 的堆区域组成,每个 ZPage 都与提交的堆内存的可变数量相关联。当 ZGC 压缩堆时,ZPages 被释放并插入到页面缓存 ZPageCache 中,页面缓存中的 ZPages 可以重新使用,以满足新的堆分配。


ZPageCache 中的 ZPages 集合代表堆中未使用的部分,这部分可以释放回操作系统。ZPageCache 中的 ZPages 根据 LRU(最近最少使用)排序,并按照大中小进行分组。这样的话就可以根据算法按顺序释放未使用的内存。


Java13 还提供了-XX:ZUncommitDelay=<seconds>命令,用于指定释放多长时间(默认是 5 分钟)未使用的内存,这个参数类似于 Shenandoah 中的-XX:ShenandoahUncommitDelay=<milliseconds>


在 Java13 中,ZGC 内存释放功能默认开启,可通过参数-XX:-ZUncommit关闭该功能。由于 ZGC 释放内存时,不会低于最小堆内存,即当最小堆内存(-Xms)与最大堆内存(-Xmx)一样时,不会自动释放。

Socket API 重构

Java 中的 Socket 是从 Java1.0 开始就有的,是 Java 中不可或缺的网络 API,算起来已经服役 20 多年了。在这段时间内,信息技术已经发生了很多变化,这些上古 API 有一定的局限性,而且不容易维护和调试。


Java 的 Socket API 主要包括java.net.ServerSocketjava.net.SocketServerSocket用来监听连接请求的端口,连接成功后返回的是Socket对象,可以通过操作Socket对象实现数据发送和读取。Java 是通过SocketImpl实现这些功能。


在 Java13 之前,通过SocketImpl的子类PlainSocketImpl实现。在 Java13 中,引入NioSocketImpl实现,该实现以 NIO 为基础,与高速缓冲机制集成,实现非阻塞式网络。


如果想用回PlainSocketImpl,可以设置启动参数-Djdk.net.usePlainSocketImpl=true即可。

文末总结

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


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

推荐阅读


你好,我是看山。游于码界,戏享人生。如果文章对您有帮助,请点赞、收藏、关注。

发布于: 刚刚阅读数: 2
用户头像

看山

关注

🏆 InfoQ写作平台-签约作者 🏆 2017.10.26 加入

InfoQ签约作者,CSDN 博客专家,公号「看山的小屋」,专注后端开发、架构相关知识分享,个人网站 https://howardliu.cn/。

评论

发布
暂无评论
☕️从 Java8 到 Java17 的新特性(五):Java13 的新特性_Java_看山_InfoQ写作社区