Java 13 新特性全面解读

Java 13 新特性全面解读作者 l Hollis 本文经授权转载自 Hollis (ID:hollischuang)

2017 年 8 月,JCP 执行委员会提出将 Java 的发布频率改为每六个月一次,新的发布周期严格遵循时间点,将在每年的 3 月份和 9 月份发布。

Java 13 新特性全面解读

目前该版本包含的特性已经全部固定,主要包含以下五个:

JEP 350,Dynamic CDS Archives

JEP 351,ZGC: Uncommit Unused Memory

JEP 353,Reimplement the Legacy Socket API

JEP 354: Switch Expressions (Preview)

JEP 355,Text Blocks (Preview)

下面来逐一介绍下这五个重要的特性。

Java 13 新特性全面解读

Dynamic CDS Archives

这一特性是在 JEP310:Application Class-Data Sharing 基础上扩展而来的,Dynamic CDS Archives 中的 CDS 指的就是 Class-Data Sharing。

那么,这个 JEP310 是个啥东西呢?

我们知道在同一个物理机/虚拟机上启动多个 JVM 时,如果每个虚拟机都单独装载自己需要的所有类,启动成本和内存占用是比较高的。所以 Java 团队引入了 CDS 的概念,通过把一些核心类在每个 JVM 间共享,每个 JVM 只需要装载自己的应用类,启动时间减少了,另外核心类是共享的,所以 JVM 的内存占用也减少了。

CDS 只能作用于 Boot Class Loader 加载的类,不能作用于 App Class Loader 或者自定义的 Class Loader 加载的类。

在 Java 10 中,则将 CDS 扩展为 AppCDS,顾名思义,AppCDS 不止能够作用于 Boot Class Loader 了,App Class Loader 和自定义的 Class Loader 也都能够起作用,大大加大了 CDS 的适用范围。也就说开发自定义的类也可以装载给多个 JVM 共享了。

Java 10 中包含的 JEP310 的通过跨不同 Java 进程共享公共类元数据来减少了内存占用和改进了启动时间。

但是,JEP310 中,使用 AppCDS 的过程还是比较复杂的,需要有三个步骤:

1、决定要 Dump 哪些 Class

2、将类的内存 Dump 到归档文件中

3、使用 Dump 出来的归档文件加快应用启动速度

这一次的 JDK 13 中的 JEP 350 ,在 JEP310 的基础上,又做了一些扩展。允许在 Java 应用程序执行结束时动态归档类,归档类将包括默认的基础层 CDS (class data-sharing)存档中不存在的所有已加载的应用程序类和库类。

也就是说,在 Java 13 中再使用 AppCDS 的时候,就不在需要这么复杂了。

Java 13 新特性全面解读

ZGC: Uncommit Unused Memory

在讨论这个问题之前,想先问一个问题,JVM 的 GC 释放的内存会还给操作系统吗?

GC 后的内存如何处置,其实是取决于不同的垃圾回收器的。因为把内存还给 OS,意味着要调整 JVM 的堆大小,这个过程是比较耗费资源的。

在 JDK 11 中,Java 引入了 ZGC,这是一款可伸缩的低延迟垃圾收集器,但是当时只是实验性的。并且,ZGC 释放的内存是不会还给操作系统的。

Java 13 新特性全面解读

而在 Java 13 中,JEP 351 再次对 ZGC 做了增强,本次 ZGC 可以将未使用的堆内存返回给操作系统。之所以引入这个特性,是因为如今有很多场景中内存是比较昂贵的资源,在以下情况中,将内存还给操作系统还是很有必要的:

  • 1、那些需要根据使用量付费的容器

  • 2、应用程序可能长时间处于空闲状态并与许多其他应用程序共享或竞争资源的环境。

  • 3、应用程序在执行期间可能有非常不同的堆空间需求。例如,启动期间所需的堆可能大于稍后在稳定状态执行期间所需的堆。

Java 13 新特性全面解读

Reimplement the Legacy Socket API

使用易于维护和调试的更简单、更现代的实现替换 java.net.Socket 和 java.net.ServerSocket API。

java.net.Socket 和 java.net.ServerSocket 的实现非常古老,这个 JEP 为它们引入了一个现代的实现。现代实现是 Java 13 中的默认实现,但是旧的实现还没有删除,可以通过设置系统属性 jdk.net.usePlainSocketImpl 来使用它们。

运行一个实例化 Socket 和 ServerSocket 的类将显示这个调试输出。这是默认的 (新的):
[code]
java -XX:+TraceClassLoading JEP353 | grep Socket
[0.033s][info ][class,load] java.net.Socket source: jrt:/java.base
[0.035s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.035s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
[0.042s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.042s][info ][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base
[0.043s][info ][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base
[0.044s][info ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.045s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.045s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
[/code]
[code]
上面输出的 sun.nio.ch.NioSocketImpl 就是新提供的实现。
[/code]

如果使用旧的实现也是可以的(指定参数 jdk.net.usePlainSocketImpl):
[code]
$ java -Djdk.net.usePlainSocketImpl -XX:+TraceClassLoading JEP353 | grep Socket
[0.037s][info ][class,load] java.net.Socket source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.043s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
[0.046s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.PlainSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base
[0.047s][info ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
[0.047s][info ][class,load] java.net.SocketOption source: jrt:/java.base
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
[0.048s][info ][class,load] jdk.net.LinuxSocketOptions source: jrt:/jdk.net
[0.048s][info ][class,load] jdk.net.LinuxSocketOptions$$Lambda$2/0x0000000800b51040 source: jdk.net.LinuxSocketOptions
[0.049s][info ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
[0.049s][info ][class,load] java.net.StandardSocketOptions source: jrt:/java.base
[0.049s][info ][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base
[0.051s][info ][class,load] sun.net.ext.ExtendedSocketOptions$$Lambda$3/0x0000000800b51440 source: sun.net.ext.ExtendedSocketOptions
[0.057s][info ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
[0.057s][info ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
[0.058s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.058s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.058s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
[/code]

上面的结果中,旧的实现 java.net.PlainSocketImpl 被用到了。

Java 13 新特性全面解读

Switch Expressions (Preview)

在 JDK 12 中引入了 Switch 表达式作为预览特性。JEP 354 修改了这个特性,它引入了 yield 语句,用于返回值。这意味着,switch 表达式 (返回值) 应该使用 yield, switch 语句 (不返回值) 应该使用 break。

在以前,我们想要在 switch 中返回内容,还是比较麻烦的,一般语法如下:
[code]
int i;
switch (x) {
case "1":
i=1;
break;
case "2":
i=2;
break;
default:
i = x.length();
break;
}
[/code]
[/code]

在 JDK13 中使用以下语法:
[code]
int i = switch (x) {
case "1" -> 1;
case "2" -> 2;
default -> {
int len = args[1].length();
yield len;
}
};
[/code]
[/code]

或者
[code]
int i = switch (x) {
case "1": yield 1;
case "2": yield 2;
default: {
int len = args[1].length();
yield len;
}
};
[/code]
[/code]

在这之后,switch 中就多了一个关键字用于跳出 switch 块了,那就是 yield,他用于返回一个值。和 return 的区别在于:return 会直接跳出当前循环或者方法,而 yield 只会跳出当前 switch 块。

Java 13 新特性全面解读

Text Blocks (Preview)

在 JDK 12 中引入了 Raw String Literals 特性,但在发布之前就放弃了。这个 JEP 在引入多行字符串文字(text block)在意义上是类似的。

text block,文本块,是一个多行字符串文字,它避免了对大多数转义序列的需要,以可预测的方式自动格式化字符串,并在需要时让开发人员控制格式。

我们以前从外部 copy 一段文本串到 Java 中,会被自动转义,如有一段以下字符串:
[code]


Hello, Hollisp>
body>
html>
[/code]
[/code]

将其复制到 Java 的字符串中,会展示成以下内容:
[code]
"\n" +
" \n" +
"

Hello, Hollisp>\n" +
" body>\n" +
"html>\n";
[/code]
[/code]

即被自动进行了转义,这样的字符串看起来不是很直观,在 JDK 13 中,就可以使用以下语法了:
[code]
"""

Hello, Hollis




""";

[/code]
[/code]

使用 """ 作为文本块的开始符合结束符,在其中就可以放置多行的字符串,不需要进行任何转义。看起来就十分清爽了。

如常见的 SQL 语句:
[code]
String query = """
SELECT EMP_ID, LAST_NAME FROM EMPLOYEE_TB
WHERE CITY = 'INDIANAPOLIS'
ORDER BY EMP_ID, LAST_NAME;
""";
[/code]
[/code]

看起来就比较直观,清爽了。

Java 13 新特性全面解读

总结

以上,就是 JDK13 中包含的 5 个特性,能够改变开发者的编码风格的主要有 Text Blocks 和 Switch Expressions 两个新特性,但是这两个特性还处于预览阶段。

而且,JDK13 并不是 LTS (长期支持)版本,如果你正在使用 Java 8 (LTS)或者 Java 11 (LTS),暂时可以不必升级到 Java 13。

Java 13 新特性全面解读参考资料: https://openjdk.java.net/projects/jdk/13/ https://metebalci.com/blog/what-is-new-in-java-13/https://www.jianshu.com/p/890196bf529a

作者简介:Hollis,知名技术博主,个人博客文章阅读量数百万,CSDN 博客专家。工作地点杭州,主要从事金融,支付领域的 Java 开发,热衷于技术分享。

本文转载自 Hollis 公众号,专注原创技术文章,主要以 Java 相关技术为主,覆盖基础知识、底层原理、技术成长等话题。

【END】

Java 13 新特性全面解读

热 文 推 荐

荣耀赵明 “Diss” 5G 手机;甲骨文创始人埃里森:Uber 一文不值;Chrome 77 发布 | 极客头条
华为此时就把 5G 芯片用在手机上,垒起了多高的竞争壁垒?
☞海阔凭鲲跃 天高任鹏飞——从一场大赛看华为云的鲲鹏之“智”☞我愿出 2 倍工资,挖这个被裁的程序员!☞阿里云智能运维的自动化三剑客免费开源!新学期必收藏的 AI 学习资源,从课件、工具到源码都齐了华为 | 泰山之巅 鲲鹏展翅 扶摇直上九万里
鸿蒙 OS 的到来,能为我们改变什么?| 程序员大本营 9 月刊厉害!接班马云的为何是张勇?Java 13 新特性全面解读你点的每个“在看”,我都认真当成了喜欢

来源链接:mp.weixin.qq.com