Java 12将提供39种新功能

来自Habré的精彩采访中:“ Simon Ritter从一开始就是从事Java的人,并继续担任Azul的副技术总监,Azul是Zing JVM虚拟机的公司,也是最好的垃圾收集器之一,C4(连续并发压缩)收藏者)»
以下是他的文章翻译,其中涉及新的JDK 12功能以及在迁移到新版本时可能遇到的一些困难。

我写了几篇博客文章,列出了每个最新Java版本( JDK 10JDK 11 )的所有更改。 现在,我将探讨JDK 12的阴暗面,重点介绍一些如果您希望将应用程序移植到此版本时可能导致问题的陷阱。



到目前为止,JDK 12拥有的所有新版本中新功能的数量最少(我在JDK 10中数为109,而在JDK 11中数为90)。 这还不错-由于发布周期的缘故,某些版本将包含更多更改,而某些包含更少更改。


我将把新功能分解为明显的逻辑区域:Java,库,JVM和其他JDK函数。


语言变更


我(而且我认为还有很多其他人)会认为在JDK 12中最引人注意的功能是新的switch语句( JEP 325 )。 这也是首次用于“预览”功能的语言更改。 作为JEP 12的一部分,“预览”的想法于2018年初引入。 从本质上讲,这是使用命令行选项启用Beta版新功能的方法。 使用预览,仍然可以根据用户反馈进行更改,并且在最坏的情况下,如果未正确接收功能,则将其完全删除。 预览功能的关键是它们不包含在Java SE规范中。 关于新开关,Habré上有一个很好的翻译
在JDK 12中,开关已成为一种表达式,用于评估其“内容”以产生结果。 我将立即说明,这不会影响向后兼容性,因此您无需更改任何将switch用作运算符的代码。


我将使用来自JEP的示例,因为它简单明了:


旧开关
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("Huh? " + day); } 

如您所见,我们将星期几映射到变量day的名称,然后分配值numLetters 。 现在,switch是一个运算符,我们可以使用switch语句的结果进行一次赋值(显着减少错误代码的可能性):


 int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> throw new IllegalStateException("Huh? " + day); }; 

您将很快注意到两个语法更改。 OpenJDK开发人员偶然发现了一个鲜为人知的语法功能,称为逗号分隔列表。 同样,lambda表达式运算符->使返回值更容易。 如果确实需要,您仍然可以使用带有值的break 。 关于此功能还有其他一些细节,但是阅读JEP可能更容易。


图书馆


我发现有一项更改非常有用。 还有许多次要的。


发球收集器


与往常一样,Streams API具有一个由Collectors实用程序类提供的新Collector。 可以使用teeing()方法获得新的收集器。 发球收集器采用三个参数:两个收集器和一个双功能。 为了了解此收集器的工作,我建议在Habré上阅读这篇文章
为了理解他是如何做到的,我画了一张图:


图片

输入流中的所有值都传递给每个收集器。 每个收集器的结果都作为参数传递给BiFunction并生成最终结果。


一个简单的示例正在计算平均值(是的,我知道已经有用于此目的的收集器,例如averagingInt() ,但这是一个有助于理解该概念的简单示例)。


 /* Assume Collectors is statically imported */ double average = Stream.of(1, 4, 2, 7, 4, 6, 5) .collect(teeing( summingDouble(i -> i), counting(), (sum, n) -> sum / n) ); 

第一个收集器计算输入流的总和,第二个收集器计算元素的数量。 BiFunction将总和除以元素数即可得到平均值。


java.io


InputStream skipNBytes(long n) -跳过并从输入流InputStream中精确地丢弃n个字节。 如果n为零或更少,则不跳过字节。


java.lang


出现了一个新程序包java.lang.constant,它是常量JVM API JEP 334的一部分


每个Java类文件都有一个持久性池,该池存储该类中字节码指令的操作数。 由于加载类存在问题,开发人员很难操纵类文件。 常量JVM API提供了符号引用类型来描述常量的每种形式(类,可加载常量, MethodHandleMethodHandle常量, MethodType常量)。


它还影响了其他几个类别。 现在,以下所有类都具有describeConstable()方法:


  • 班级
  • 双倍
  • 枚举
  • 浮点数
  • 整数
  • 长的
  • 弦乐
  • 方法句柄
  • 方法类型
  • 瓦尔汉德尔

作为英国人,我觉得这很有趣。 自11世纪以来, describeConstable使用术语Constable, describeConstable ,这就是我们经常提到警察的方式。 它也是18世纪著名画家John Constable的名字。 这使我想知道describeTurner()方法是否在将来的版本中。 显然,在这种情况下,它是Constant Table的缩写,与律师或风景画家无关。


现在,以下类包括resolveConstantDesc()方法:


  • 双倍
  • 枚举
  • 浮点数
  • 整数
  • 长的
  • 弦乐

java.lang.Character


内部类已更新,以包括新的Unicode块。 我总是喜欢看看人们发现要添加到Unicode中的内容,下面是一些示例:


  • 象棋符号
  • 玛雅数字
  • Sogdian是伊朗东部的一种语言,在11世纪不再使用。
  • 旧的Sogdian是Sogdian的旧版本(而且,我怀疑是更有限的版本)

java.lang.Class


arrayType()返回Class ,该数组的组件类型由此Class描述。 可以使用jshell进行验证:


 jshell> (new String[2]).getClass().getName() $11 ==> "[Ljava.lang.String;" jshell> (new String[2]).getClass().arrayType() $12 ==> class [[Ljava.lang.String; jshell> "foo".getClass().arrayType() $15 ==> class [Ljava.lang.String; 

我不太确定此方法的含义,因为它所做的只是向该类表示的类型添加一个Class


componentType()getComponentType()相同。 问题是-为什么要添加冗余方法?


descriptorString() -再次返回与getName()相同的结果。 但是,这是必需的,因为Class现在实现了与新的常量JVM API相关联的TypeDescriptor接口。


lava.lang.String


indent() -在字符串中添加一系列前导空格。 如果参数为负,则将删除此前导空格数(如果可能)。


transform() -将提供的函数应用于字符串。 结果可能不是字符串。


java.lang.invoke


VarHandle现在具有toString()以返回紧凑的描述。


java.net.SecureCacheResponsejava.net.ssl.HttpsConnection具有一个新方法getSSLSession() ,该方法返回Optional其中包含用于连接的SSLSession


java.nio.files


Files类具有一个新方法mismatch() ,该方法查找并返回两个文件内容中第一个不匹配字节的位置,如果没有不匹配,则返回-1L。


java.text


有一个新的类CompactNumberFormat 。 这是NumberFormat的子类,它以紧凑形式格式化十进制数。 紧凑格式的示例1M而不是1000000 ,因此需要两个字符而不是九个字符。 NumberFormatjava.text.spi.NumberFormatProvider已扩展为包括新的getCompactNumberInstance()方法。 还有一个新的枚举NumberFormatStyle ,它具有两个含义:LONG和SHORT。


java.util.concurrent


现在,CompletionStage包含使用三种方法的几种重载形式:


  • 异常异步
  • 非常出色
  • 异常ComposeAsync

这些方法扩展了从现有的CompletionStage创建新的CompletionStage的可能性,如果当前的CompletionStage以例外结尾。 有关详细信息,请参阅API文档。


javax.crypto


Cipher类具有一个新的toString()方法,该方法返回一个包含Cipher转换,模式和提供程序的字符串。


javax.naming.ldap.spi


这是JDK 12中的新软件包,它包含两个类: LdapDnsProvider和LDAP URL的DNS查找结果LdapDnsProviderResults LDAP操作期间DNS查找的提供程序类。


秋千


秋千仍在更新! 是的, filechooser.FileSystemView现在具有一个新的getChooserShortcutPanelFiles()方法。 它返回代表值的文件数组,这些值默认情况下显示在文件选择快捷方式栏中。


JVM更改


JEP 189:雪兰多亚 :低暂停时间垃圾收集器


Shenandoah是Red Hat在2014年宣布的一项研究项目,专注于对JVM中内存管理的低延迟应用程序需求。 对于超过20 GB的堆,其目标是最大暂停时间为1..10毫秒( 因此,它不适用于小型应用 -作为Shenandoah的开发人员之一回答 ,事实并非如此,它在小型应用程序方面做得很好。 该收集器旨在与应用程序线程并行工作,因此避免了我们在大多数垃圾收集器中看到的问题。


JEP 344:G1混合收藏


此更改旨在改善G1收集器达到设定的延迟目标时的行为。 G1将堆空间(旧的和旧的)划分为多个区域。 这个想法是,在老一代中,您不需要一次操作就可以收集垃圾。 当G1需要收集垃圾时,它将选择其定义的区域。 这称为收集套件。 在JDK 12之前,当工作开始进行时,实质上所有工作必须作为原子操作完成。 问题在于,有时由于应用程序对堆空间的使用发生了变化,导致收集集过大且花费了太多时间进行收集,从而导致无法达到暂停时间。


在JDK 12中,如果G1识别出这种情况,如果这不会影响应用程序继续为新对象分配空间的能力,它将中途中断数据收集。 当达到短暂停时间时,G1的最终效果会更好。


JEP 346:从G1快速返回未使用的已分配内存


这是G1的另一项性能改进,但另一项与JVM与系统其余部分的交互方式有关。 显然,JVM堆需要内存,并且在启动时,它会从操作系统虚拟内存分配器请求内存。 当应用程序启动时,有时堆所需的内存量会减少,并且分配的内存的一部分可以返回给操作系统,以供其他应用程序使用。


G1已经做到了,但是只能在两个地方之一中做到这一点。 首先,在完整的收集过程中,其次,在并行循环之一中。 G1尝试不进行完整的收集,并且由于内存使用率较低,因此在收集周期之间可能会有很大的时间间隔。 这导致G1可以长时间保留固定内存。


在JDK 12中,G1将在应用程序空闲时定期尝试继续或运行并行循环,以确定Java堆的总体用途。 未使用的内存可以以更及时和可预测的方式返回到操作系统。


新的命令行标志-XX:G1PeriodicGCInterval可用于设置-XX:G1PeriodicGCInterval检查之间的毫秒数。


对于长时间闲置的应用程序,此功能将导致更保守地使用JVM内存。


其他JDK新功能


JEP 230:微基准测试套件


Java Microbenchmarking Harness(JMH)由Alexey Shipilev在Oracle工作时开发,并为开发Java应用程序性能测试提供了广泛的平台。 Alexey做得非常出色,可以帮助人们避免尝试分析应用程序性能时犯的许多简单错误:预热,避免异常等。


现在,JMH可以包含在OpenJDK中。 任何对JDK本身感兴趣并更改代码的人都可以使用它来比较更改前后的性能,以及比较不同发行版中的性能。 包括许多测试以启用测试; JMH的设计易于在需要的地方添加新测试。


JEP 340:一个Aarch64端口,而不是两个


OpenJDK有两个用于Arm64架构的端口,一个由Oracle提供,另一个由Red Hat提供。 由于这不是必需的,并且Oracle停止为其JDK二进制文件支持Arm,因此决定仅使用Red Hat端口,该端口仍受支持和开发。


JEP 341:默认CDS存档


数据共享(CDS)类曾经是Oracle JDK中的一项商业功能。 由于JDK 11中进行了最近的转换,以消除Oracle JDK和OpenJDK之间的所有功能差异,因此它已包含在OpenJDK中。


要使用CDS,您需要为在应用程序启动时加载的类创建一个存档。 现在,用于64位平台的JDK 12在lib/server目录中具有classes.jsa文件。 这是“默认类别”的CDS存档。 我认为这意味着JDK模块中的所有公共类。 我找不到解压缩包装的方法。 由于默认情况下启用CDS,它等效于命令行上的-Xshare:auto选项,因此用户将从中受益于缩短的应用程序启动时间。


结论


JDK 12提供了少量新功能和API,其中switch对于开发人员来说是最有趣的。 G1用户肯定会赞赏其性能改进。


对于新版本,我建议所有用户在此版本中测试他们的应用程序。 如果您决定继续使用下一个长期支持版本,则跟踪增量更改将有助于您避免意外。


我们为Zulu社区版提供了免费的JDK 12版本以帮助您进行测试。 一定要尝试一下。

Source: https://habr.com/ru/post/zh-CN446590/


All Articles