一 背景概述
1.1 Java现状
2020 年是 Java 诞生的第 25 个年头。Oracle表示,目前java仍然流行编程语言,广泛使用在企业中。目前全世界有超过69%的专职开发人员使用Java,全世界有510亿台活动Java虚拟机(JVM)在部署中,目前Java主要被用于分析,数据管理,大数据,DevOps,移动,持续开发工具和聊天机器人等服务。
1.2 历史版本的主要新特性
在过去的这些年中,Java 在过去增强功能的推动下为用户提供了超过二十年的创新。例如:
- JDK5:enum、泛型、自动装箱与拆箱、可变参数、增强循环等
- JDK6:支持脚本语言、JDBC4.0API
- JDK7:支持try-with-resources、switch语句块增加String支持、NIO2.0包
- JDK8:lambda表达式、Stream API、新的日期时间的API、方法引用、构造器引用
- JDK9:模块化系统、jshell
- JDK10:局部变量的类型推断
- JDK11:ZGC的引入、Epsilon GC
- JDK12:switch表达式、Shenandoah GC、增强G1
- JDK13:switch表达式引入yield、文本块
- JDK14:instanceof模式识别、Records、弃用Parallel Scavenge+Serial GC组合、删除CMS GC
1.3 JDK更新相关名字介绍
1)孵化器模块(Incubator)
- 尚未定稿的API/工具,主要用于从Java社区收集使用反馈,稳定性无保障,后期有较大可能性移除。
2)预览特性(Preview)
- 规格已成型,实现已确定,但还未最终定稿。这些特性还是存在被移除的可能性,但一般来说最后都会被固定下来。
1.4 JDK更新需要注意的点
1)角度一:语法层面
如之前版本更新的Lambda表达式,switch表达式,enum,接口的静态方法,默认方法,私有方法
2)角度二:API层面
StreamAPI,新的时间日期API, Optional类,String类,集合框架
3)角度三:底层优化,Java,C/C++
JVM的优化,元空间,GC,GC的组合,GC的参数,JS的执行引擎,集合框架底层的实现
**
二 JDK15概述
2.1 主要涉及方面
1)Java 15为用户提供了14项主要的增强/更改,包括一个孵化器模块,三个预览功能,两个不推荐使用的功能以及两个删除功能。
对应中文特性:(JEP:JDK Enhancement Proposals,JDK 增强建议,也就是 JDK 的特性新增和改进提案。)
JEP 339:EdDSA 数字签名算法
JEP 360:密封类(预览)
JEP 371:隐藏类
JEP 372:移除 Nashorn JavaScript 引擎
JEP 373:重新实现 Legacy DatagramSocket API
JEP 374:禁用偏向锁定
JEP 375:instanceof 模式匹配(第二次预览)
JEP 377:ZGC:一个可扩展的低延迟垃圾收集器
JEP 378:文本块
JEP 379:Shenandoah:低暂停时间垃圾收集器
JEP 381:移除 Solaris 和 SPARC 端口
JEP 383:外部存储器访问 API(第二次孵化版)
JEP 384:Records(第二次预览)
JEP 385:废弃 RMI 激活机制
2)总结:JDK15整体来看新特性方面并不算很亮眼,它主要是对之前版本预览特性的功能做了确定,如文本块、ZGC等,这么一来我们就可以放心大胆的使用了。
2.2 JDK15代码主要贡献者
1)通过Oracle的官方博文看到,主力军Oracle干了79.68%的工作量,其次是开源巨头红帽。有不少企业参与其中。
2)国内企业贡献最多的居然是腾讯公司,被 Oracle 点名感谢。
3)阿里巴巴和华为也做出了不可磨灭的贡献。Java 有中国科技力量的贡献能大大提升中国 IT 的硬实力。
2.3 支持JDK15的开发工具
1)JetBrains IDEA
2)Apache NetBeans
3)Eclipse Marketplace
注意:idea需要2020.2版本以上才支持
JDK15下载路径:
https://www.oracle.com/java/technologies/javase-jdk15-downloads.html
IDEA 2020.02版本下载地址:
https://www.jetbrains.com/idea/download/#section=windows
三 主要的新特性
主要关注的6个新特性,开发可能需要使用
3.1 密封类
JEP 360:Sealed Classes(Preview)密封的类和接口(预览)
1)通过密封的类和接口来增强 Java 编程语言,这是新的预览特性。用于限制超类的使用,密封的类和接口限制其它可能继承或实现它们的其它类或接口。
2)这个特性的目标包括——允许类或接口的开发者来控制哪些代码负责实现,提供了比限制使用超类的访问修饰符声明方式更多选择,并通过支持对模式的详尽分析而支持模式匹配的未来发展。
3)在Java中,类层次结构通过继承实现代码的重用,父类的方法可以被许多子类继承。但是,类层次结构的目的并不总是重用代码。有时,其目的是对域中存在的各种可能性进行建模,例如图形库支持的形状类型或金融应用程序支持的贷款类型。当以这种方式使用类层次结构时,我们可能需要限制子类集从而来简化建模。
4)具体使用:
因为我们引入了sealed class或interfaces,这些class或者interfaces只允许被指定的类或者interface进行扩展和实现。
5)使用修饰符sealed,您可以将一个类声明为密封类。密封的类使用reserved关键字permits列出可以直接扩展它的类。子类可以是最终的,非密封的或密封的。
public sealed interface Person permits Teacher, Student, Worker {
/**
* 使用两个关键字
* sealed:表明当前接口/类是一个密封接口/类,只能被特定的类实现/集成
* permits:表明后面的类可以实现/继承当前接口
*/
}
final class Teacher implements Person {
// final类可以实现密封类/接口
}
sealed class Student implements Person permits GoodStudent {
// 密封类可以实现密封类
}
final class GoodStudent extends Student {
//
}
non-sealed class Worker implements Person {
// non-sealed表明当前类是一个普通类
}
3.2 隐藏类
JEP 371:Hidden Classes(隐藏类)
1)该提案通过启用标准 API 来定义无法发现且具有有限生命周期的隐藏类,从而提高 JVM 上所有语言的效率。JDK内部和外部的框架将能够动态生成类,而这些类可以定义隐藏类。通常来说基于JVM的很多语言都有动态生成类的机制,这样可以提高语言的灵活性和效率。
- 隐藏类天生为框架设计的,在运行时生成内部的class。
- 隐藏类只能通过反射访问,不能直接被其他类的字节码访问。
- 隐藏类可以独立于其他类加载、卸载,这可以减少框架的内存占用。
2)Hidden Classes是什么呢?
- Hidden Classes就是不能直接被其他class的二进制代码使用的class。Hidden Classes主要被一些框架用来生成运行时类,但是这些类不是被用来直接使用的,而是通过反射机制来调用。
- 比如在JDK8中引入的lambda表达式,JVM并不会在编译的时候将lambda表达式转换成为专门的类,而是在运行时将相应的字节码动态生成相应的类对象。
- 另外使用动态代理也可以为某些类生成新的动态类。
3)那么我们希望这些动态生成的类需要具有什么特性呢?
- 不可发现性:因为我们是为某些静态的类动态生成的动态类,所以我们希望把这个动态生成的类看做是静态类的一部分。所以我们不希望除了该静态类之外的其他机制发现。
- 访问控制:我们希望在访问控制静态类的同时,也能控制到动态生成的类。
- 生命周期:动态生成类的生命周期一般都比较短,我们并不需要将其保存和静态类的生命周期一致。
4)API的支持
所以我们需要一些API来定义无法发现的且具有有限生命周期的隐藏类。这将提高所有基于JVM的语言实现的效率。
比如:
java.lang.reflect.Proxy可以定义隐藏类作为实现代理接口的代理类。
java.lang.invoke.StringConcatFactory可以生成隐藏类来保存常量连接方法;
java.lang.invoke.LambdaMetaFactory可以生成隐藏的nestmate类,以容纳访问封闭变量的lambda主体;
5)普通类是通过调用ClassLoader::defineClass创建的,而隐藏类是通过调用Lookup::defineHiddenClass创建的。这使JVM从提供的字节中派生一个隐藏类,链接该隐藏类,并返回提供对隐藏类的反射访问的查找对象。调用程序可以通过返回的查找对象来获取隐藏类的Class对象。
3.3 instanceof模式匹配
JEP 375:Pattern Matching for instanceof (Second Preview) instanceof 自动匹配模式 在Java 14 中作为预览语言功能引入的instanceof模式匹配,在Java 15 中处于第二次预览,而没有任何更改。
1)模式匹配允许程序中的通用逻辑(主要是从对象中的条件提取组件)可以更简洁地表达。Haskell 和 C# 等语言已采用模式匹配来实现简洁和安全性。
/**
* 旧方式
*/
public void a(Object obj) {
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str);
}
}
/**
* 新特性
*/
public void b(Object obj) {
if (obj instanceof String str) {
System.out.println(str.length());
}
}
3.4 ZGC功能转正
JEP 377:ZGC: A Scalable Low-Latency Garbage Collector (Production) ZGC 功能转正
1)ZGC是Java 11引入的新的垃圾收集器(JDK9以后默认的垃圾回收器是G1),经过了多个实验阶段,自此终于成为正式特性。
- 自 2018 年以来,ZGC 已增加了许多改进,从并发类卸载、取消使用未使用的内存、对类数据共享的支持到改进的 NUMA 感知。
此外,最大堆大小从 4 TB 增加到 16 TB。支持的平台包括 Linux、Windows 和 MacOS。
2)ZGC是一个重新设计的并发的垃圾回收器,通过减少 GC 停顿时间来提高性能。
但是这并不是替换默认的GC,默认的GC仍然还是G1。
- 之前需要通过-XX:+UnlockExperimentalVMOptions -XX:+UseZGC来启用ZGC,现在只需要-XX:+UseZGC就可以。
相信不久的将来它必将成为默认的垃圾回收器。
3)相关的参数有ZAllocationSpikeTolerance、ZCollectionInterval、ZFragmentationLimit、ZMarkStackSpaceLimit、ZProactive、ZUncommit、ZUncommitDelay ZGC-specific JFR events(ZAllocationStall、ZPageAllocation、ZPageCacheFlush、ZRelocationSet、ZRelocationSetGroup、ZUncommit)也从experimental变为product
3.5 文本块功能转正
JEP 378:文本块功能转正
1)Text Blocks首次是在JDK 13中以预览功能出现的,然后在JDK 14中又预览了一次,终于在JDK 15中被确定下来,可放心使用了。
2)文本块是一种多行字符串文字,它避免了大多数转义序列的需要,以一种可预测的方式自动设置字符串的格式,并在需要时使开发人员可以控制格式,简化编写 Java 程序的任务。
3)文本块建议的目标是提高 Java 程序中的字符串的可读性,这些字符串表示以非 Java 语言编写的代码。
- 另一个目标是支持从字符串文本迁移,规定任何新构造都可以表达与字符串文本相同的字符串集,解释相同的转义序列,并且以与字符串文本相同的方式进行操作。OpenJDK 开发人员希望添加转义序列来管理显式空白和换行控件。
@Test
public void test() {
String str1 = "我们都是好孩子\n" +
"天真烂漫的孩子\n" +
"我们都是向往同一个家园\n" +
"同一片蓝天";
System.out.println(str1);
// 新特性
String str2 = """
我们都是好孩子
天真烂漫的孩子
我们都是向往同一个家园
同一片蓝天
""";
System.out.println(str2);
}
3.6 Record Class
JEP 384:Records Class(预览)
1)Records Class 也是第二次出现的预览功能,它在 JDK 14 中也出现过一次了,使用 Record 可以更方便的创建一个常量类,使用的前后代码对比如下。
2)当你用Record 声明一个类时,该类将自动拥有以下功能:
- 获取成员变量的简单方法,以下面代码为例 name() 和 age() 。注意区别于我们平常getter的写法。
- 一个 equals 方法的实现,执行比较时会比较该类的所有成员属性
- 重写 equals 当然要重写 hashCode
- 一个可以打印该类所有成员属性的 toString 方法。
请注意只会有一个构造方法(全参构造器)
// 声明一个Record类
public record Customer(String name, Integer age) {
}
// 通过编译此文件为字节码,然后再反编译,得到
public final class Customer extends java.lang.Record {
private final java.lang.String name;
private final java.lang.Integer age;
public Customer(java.lang.String name, java.lang.Integer age) { /* compiled code */ }
public final java.lang.String toString() { /* compiled code */ }
public final int hashCode() { /* compiled code */ }
public final boolean equals(java.lang.Object o) { /* compiled code */ }
public java.lang.String name() { /* compiled code */ }
public java.lang.Integer age() { /* compiled code */ }
}
```java // 测试代码 public class RecordTest {
public static void main(String[] args) {
// 自动生成的全参构造器
Customer lz = new Customer("lz", 17);
// 自动生成的toString方法
System.out.println(lz.toString());
// 自动根据属性生成的get方法
System.out.println(lz.name() + "====" + lz.age());
//
}
}
```java
public record Customer(String name, Integer age) {
// 还可以声明构造器,静态变量,静态方法,实例方法
public Customer() {
// 需要调用原有的全参构造器
this(null, null);
}
public static void show() {
System.out.println("静态方法");
}
// 静态变量
public static final String NAME_SUFFIX = "_KK";
public void say(){
System.out.println("实例方法");
}
// 不可以在Record中定义实例变量
// Record类不能声明为abstract
// Record不能显式的继承于其他类
}
四 次要的新特性
以下的8个新特性是不太重要的,这些可以作为了解就可以了
4.1 EdDSA数字签名算法
JEP 339:Edwards-Curve Digital Signature Algorithm(EdDSA 数字签名算法)
1)这是一个新的功能。
- 新加入基于Edwards-Curve数字签名算法(EdDSA-Edwards-Curve Digital Signature Algorithm)的加密签名,即爱德华兹曲线数字签名算法。
- 在许多其它加密库(如 OpenSSL 和 BoringSSL)中得到支持。
2)与 JDK 中的现有签名方案相比,EdDSA 具有更高的安全性和性能,因此备受关注。
- 它已经在OpenSSL和BoringSSL等加密库中得到支持,在区块链领域用的比较多。
3)EdDSA是一种现代的椭圆曲线方案,具有JDK中现有签名方案的优点。EdDSA将只在SunEC提供商中实现。
4.2 重新实现DatagramSocket API
JEP 373:Reimplement the Legacy DatagramSocket API(重新实现 DatagramSocket API)
1)新的计划是JEP 353的后续,该方案重新实现了遗留的套接字API。
2)ava.net.datagram.Socket和java.net.MulticastSocket的当前实现可以追溯到JDK 1.0,那时IPv6还在开发中。因此,当前的多播套接字实现尝试调和IPv4和IPv6难以维护的方式。
3)通过替换 java.net.datagram 的基础实现,重新实现旧版 DatagramSocket API。
4)更改java.net.DatagramSocket 和 java.net.MulticastSocket 为更加简单、现代化的底层实现。提高了 JDK 的可维护性和稳定性。
5)通过将java.net.datagram.Socket和java.net.MulticastSocket API的底层实现替换为更简单、更现代的实现来重新实现遗留的DatagramSocket API。
6)新的实现:
- 易于调试和维护。
- 与Project Loom中正在探索的虚拟线程协同。
4.3 禁用偏向锁定
EP 374: Disable and Deprecate Biased Locking 禁用偏向锁定
1)在默认情况下禁用偏向锁定,并弃用所有相关命令行选项。目标是确定是否需要继续支持偏置锁定的高维护成本的遗留同步优化,HotSpot虚拟机使用该优化来减少非竞争锁定的开销。尽管某些Java应用程序在禁用偏向锁后可能会出现性能下降,但偏向锁的性能提高通常不像以前那么明显。
2)该特性默认禁用了biased locking(-XX:+UseBiasedLocking),并且废弃了所有相关的命令行选型(BiasedLockingStartupDelay, BiasedLockingBulkRebiasThreshold, BiasedLockingBulkRevokeThreshold, BiasedLockingDecayTime, UseOptoBiasInlining, PrintBiasedLockingStatistics and PrintPreciseBiasedLockingStatistics)
4.4 Shenandoah垃圾回收算法
1)Shenandoah垃圾回收算法终于从实验特性转变为产品特性,这是一个从 JDK 12 引入的回收算法,该算法通过与正在运行的 Java 线程同时进行疏散工作来减少 GC 暂停时间。Shenandoah 的暂停时间与堆大小无关,无论堆栈是 200 MB 还是 200 GB,都具有相同的一致暂停时间。
2)怎么形容Shenandoah和ZGC的关系呢?异同点大概如下:
- 相同点:性能几乎可认为是相同的
- 不同点:ZGC是Oracle JDK的,根正苗红。而Shenandoah只存在于OpenJDK中,因此使用时需注意你的JDK版本
3)打开方式:使用-XX:+UseShenandoahGC命令行参数打开。
4)Shenandoah在JDK12被作为experimental引入,在JDK15变为Production;之前需要通过-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC来启用,现在只需要-XX:+UseShenandoahGC即可启用
4.5 外部存储器访问API
JEP 383:Foreign-Memory Access API (Second Incubator) 外部存储器访问 API(孵化器版)
1)目的是引入一个 API,以允许 Java 程序安全、有效地访问 Java 堆之外的外部存储器。如本机、持久和托管堆。
2)有许多Java程序是访问外部内存的,比如Ignite和MapDB。该API将有助于避免与垃圾收集相关的成本以及与跨进程共享内存以及通过将文件映射到内存来序列化和反序列化内存内容相关的不可预测性。该Java API目前没有为访问外部内存提供令人满意的解决方案。但是在新的提议中,API不应该破坏JVM的安全性。
3)Foreign-Memory Access API在JDK14被作为incubating API引入,在JDK15处于Second Incubator,提供了改进。
**
4.6 移除Solaris 和 SPARC 端口
JEP 381:Remove the Solaris and SPARC Ports (移除 Solaris 和 SPARC 端口)
1)删除对Solaris/SPARC、Solaris/x64和Linux/SPARC端口的源代码和构建支持,在JDK 14中被标记为废弃,在JDK15版本正式移除。
2)许多正在开发的项目和功能(如Valhalla、Loom和Panama)需要进行重大更改以适应CPU架构和操作系统特定代码。
3)近年来,Solaris 和 SPARC 都已被 Linux 操作系统和英特尔处理器取代。放弃对 Solaris 和 SPARC 端口的支持将使 OpenJDK 社区的贡献者能够加速开发新功能,从而推动平台向前发展。
**
4.7 移除 the Nashorn JS引擎
JEP 372:Remove the Nashorn JavaScript Engine
1)Nashorn是在JDK提出的脚本执行引擎,该功能是 2014 年 3 月发布的 JDK 8 的新特性。在JDK11就已经把它标记为废弃了,JDK15完全移除。
2)在JDK11中取以代之的是GraalVM。GraalVM是一个运行时平台,它支持Java和其他基于Java字节码的语言,但也支持其他语言,如JavaScript,Ruby,Python或LLVM。性能是Nashorn的2倍以上。
3)JDK15移除了Nashorn JavaScript Engine及jjs 命令行工具。具体就是jdk.scripting.nashorn及jdk.scripting.nashorn.shell这两个模块被移除了。
4)Graal VM在HotSpot VM基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用。语言包括:Java、Scala、Groovy、Kotlin;C、C++、JavaScript、Ruby、Python、R等
4.8 废弃 RMI 激活机制
JEP 385:Deprecate RMI Activation for Removal
1)RMI Activation被标记为Deprecate,将会在未来的版本中删除。RMI激活机制是RMI中一个过时的部分,自Java 8以来一直是可选的而非必选项。RMI激活机制增加了持续的维护负担。RMI的其他部分暂时不会被弃用。
2)RMI jdk1.2引入,EJB
3)在RMI系统中,我们使用延迟激活。延迟激活将激活对象推迟到客户第一次使用(即第一次方法调用)之前。
既然RMI Activation这么好用,为什么要废弃呢?
- 因为对于现代应用程序来说,分布式系统大部分都是基于Web的,web服务器已经解决了穿越防火墙,过滤请求,身份验证和安全性的问题,并且也提供了很多延迟加载的技术。
- 所以在现代应用程序中,RMI Activation已经很少被使用到了。并且在各种开源的代码库中,也基本上找不到RMI Activation的使用代码了。
- 为了减少RMI Activation的维护成本,在JDK8中,RMI Activation被置为可选的。现在在JDK15中,终于可以废弃了。
五 无关紧要的新特性
5.1 添加项
1)Support for Unicode 13.0 (JDK-8239383):升级了Unicode,支持Unicode 13.0
2)Added isEmpty Default Method to CharSequence (JDK-8215401):给CharSequence新增了isEmpty方法 java.base/java/lang/CharSequence.java
3)Specialized Implementations of TreeMap Methods (JDK-8176894):JDK15对TreeMap提供了putIfAbsent, computeIfAbsent, computeIfPresent, compute, merge方法提供了overriding实现
4)New Option Added to jcmd for Writing a gzipped Heap Dump (JDK-8237354):jcmd的GC.heap_dump命令现在支持gz选型,以dump出gzip压缩版的heap;compression level从1(fastest)到9(slowest, but best compression),默认为1
**
5.2 移除项和废弃项
(移除项)
1)Obsolete -XX:UseAdaptiveGCBoundary (JDK-8228991):淘汰了-XX:UseAdaptiveGCBoundary
(废弃项)
2)Deprecated -XX:ForceNUMA Option (JDK-8243628):废弃了ForceNUMA选项
3)Disable Native SunEC Implementation by Default (JDK-8237219):默认禁用了Native SunEC Implementation
5.3 其他项
(已知问题)
1)java.net.HttpClient Does Not Override Protocols Specified in SSLContext Default Parameters (JDK-8239594):HttpClient现在没有覆盖在SSLContext Default Parameters中指定的Protocols
(其他事项)
1)DatagramPacket.getPort() Returns 0 When the Port Is Not Set (JDK-8237890):当DatagramPacket没有设置port的时候,其getPort方法返回0
2)Improved Ergonomics for G1 Heap Region Size (JDK-8241670):优化了默认G1 Heap Region Size的计算