JDK 16在2021年2月18日已完成最终候选版本,并于2021年3月16日正式发布。
和JDK 15同样,JDK 16也会是一个短时间版本,仅支持六个月。
而计划在2021年9月发布的JDK 17将会是一个长期支持(LTS)版本,并得到数年的支持。
虽然JDK 16是个短时间版本,而且大部分的企业或者项目还依然停留在2018年9月发布的JDK 11(甚至更早的于2014年3月发布的JDK 8),但不妨碍Javaer对新版JDK的期待与持续学习的热情。
新特性一览
在开始以前,先让咱们来一块儿浏览一下JDK 16版本所带来的17个新特性吧。
▐ 本文将解读的新特性
357: OpenJDK源代码仓库从Mercurial迁移至Git。努力推进这一改变,将会在版本控制系统元数据大小、可用工具以及托管等方面体现优点。
369: 迁移到GitHub,这个变化是基于OpenJDK源码库迁移至Git的,JDK 16源代码仓库将出如今最流行的程序员社交网站上。
386: 在x64和AArch64架构上,将JDK移植到Alpine Linux和其余使用musl做为其主要C库的Linux发行版。Musl是 ISO C和Posix标准中描述的标准库功能的Linux实现。Alpine Linux因为其镜像小而被普遍应用于云部署、微服务以及容器环境中。Linux版本的Docker容器镜像小于6MB。让Java在此类设置中开箱即用地运行,并容许Tomcat、Jetty、Spring和其它流行的框架在这些环境中工做。经过使用jlink来减小Java运行时的大小,用户能够建立一个更小的镜像,以运行特定的应用程序。
394: instanceof操做符的模式匹配,在JDK 14和JDK 15中都已预览过,将于JDK 16最终肯定。模式匹配使程序中的通用逻辑(即从对象中有条件的提取组件)能够更简洁、更安全的表达。
395: 提供Record记录类,做为不可变数据的透明载体。
▐ 其余的新特性
347: 启用C++ 14语言功能,容许在JDK C++源代码中使用C++ 14功能,并提供有关在HotSpot代码中可使用哪些功能的具体指导。
376: 将ZGC(可扩展低延迟垃圾收集器)线程堆栈处理从安全点移至并发阶段。ZGC垃圾收集器旨在使HotSpot中的GC暂停和可伸缩性问题成为过去。
380: 添加Unix-Domain Socket Channels,其中Unix-Domain(AF_UNIX)套接字的支持被添加到nio.channels包中的Socket Channel和Server Socket Channel API中。
387: 弹性Metaspace功能可将未使用的HotSpot虚拟机的Class Metadata(Metaspace)占用的内存更迅速的返回给操做系统,从而减小Metaspace的占用并简化Metaspace的代码以下降维护成本。
388: 将JDK移植到Windows/AArch64平台。
389: 孵化阶段的外部连接程序API,支持静态类型的纯Java方式访问本地代码。此计划的目的在于经过用更高级的纯Java开发模式来替换JNI(Java本机接口),以提供与C语言的交互。它的性能将会比JNI更加优越。
390: 基于值的类的警告建议:将原始包装类指定为基于值的类,弃用其构造函数以进行移除,并提示新的弃用警告。在Java平台中对于任何基于值的类的实例进行同步的错误尝试会予以警告。
392: 提供用于打包独立的Java应用程序的jpackage工具。
396: 默认状况下,JDK内部结构是强封装的,而关键内部API(例如misc.Unsafe)除外。此计划的目标包括提升JDK的安全性和可维护性,并鼓励开发人员从直接使用内部元素逐渐迁移为使用标准API,这样开发人员和最终用户均可以轻松地升级到 Java 的将来版本。
397: 以前在JDK 15中进行过预览,JDK 16中二次预览的密封类和接口限制了能够扩展或实现它们的类和接口。此计划的目标包括容许类或接口的建立者控制负责实现它的代码,提供比访问修饰符更声明性的方式来限制超类的使用,并经过提供模式分析基础来支持模式匹配的将来发展。
338: 孵化阶段的矢量API(JDK将配备一个孵化器模块),jdk.incubator.vector,以表达在可支持的CPU架构上编译为最佳硬件指令的矢量计算,以实现优于等效标量计算的性能。
393: 孵化阶段的外部存储器访问API,容许Java程序安全的访问Java堆外的外部存储器(包括本地、持久化介质以及托管堆存储器)。
如上新特性前编号为JDK Enhancement Process的标识符,详见文末参考资料
当即尝鲜
浏览完17个新特性后,我都火烧眉毛的想尝试一下JDK 16,以及其中一些对工程上有所帮助的特性了。
那么先经过JDK官网进行
JDK 16候选版下载(http://jdk.java.net/16/)
。
因为要方便的在系统中针对多个JDK版本进行切换,可使用
jenv(https://github.com/jenv/jenv)
。
咱们把下载好的JDK16路径添加到jenv,在作以下设置便可使用。
jenv add ${JDK16_Path}jenv global openjdk64-16
若是一切顺利,那么查看JDK版本时,会有相似以下信息的返回。
java -versionopenjdk version "16"2021-03-16OpenJDK Runtime Environment (build 16+36-2231)OpenJDK 64-Bit Server VM (build 16+36-2231, mixed mode, sharing)
若是你在使用较早的IDEA版本做为开发工具,那么使用JDK 16运行程序时,可能收到以下的错误:
Cannot determine path to 'tools.jar' library for 16 (path/to/jdk-16) when running from IDEA, you should update to the latest version.
这是因为JDK9对Java运行时作了重构,已删除了rt.jar、tools.jar、dt.jar以及其它各类内部JAR包。而在较早的开发工具一般对这类JAR包有依赖,经过升级IDEA能够解决。
到官网获取一个
IDEA 2021.1 EAP预发版本
(https://www.jetbrains.com/zh-cn/idea/nextversion/)
来提早体验(也能够等待2021.3的正式版本)。
新特性解读
▐ 迁移到GitHub
早在2020年9月,OpenJDK已将Github上的jdk仓库做为JDK 16源码的主读取/写入仓库。随着JDK 16的正式发布,这将是OpenJDK在Github上开发完成的初代JDK版本。
而促使将OpenJDK源代码仓库从Mercurial迁移到Git的三个主要缘由:版本控制系统元数据,可用工具和可用托管的大小。
版本控制元数据大小方面,转换后的存储库的初始原型已显示出版本控制元数据的大小显着减小。例如,使用Git的jdk仓库的.git目录大约为300MB,而使用Mercurial的.hg目录大约为1.2GB。减小元数据可保留本地磁盘空间并减小克隆时间,同时减小传输的数据。javascript
可用工具方面,与Mercurial相比,Git可用的工具更多。全部的文本编辑器均可以本地或经过插件实现Git集成。此外,几乎全部的IDE都带有Git集成,包括Eclipse、Visual Studio、IDEA。php
可用托管方面,有许多选项可用于托管Git仓库,不管是自托管仍是做为服务托管。使用外部源码托管提供程序的缘由包括性能、与开发人员进行交互的Web API的访问权限控制 以及 蓬勃发展的社区。html
OpenJDK迁移到Github以后,对于Java开发者而言仍是有很多的便利:
经过fork一份JDK 16源码仓库(https://github.com/openjdk/jdk),能够一边阅读源代码,一边作笔记并提交,方便持续学习JDK源码。使用Git的upsteam保持JDK源码的更新,同时也保持自我更新。java
如网速够快,经过Github在线阅读代码的工具Github1s(https://github.com/conwnet/github1s),快速在浏览器中翻阅JDK 16源码(https://github1s.com/openjdk/jdk/releases/tag/jdk-16%2B35)也是很是方便。linux
若是是在IDEA下工做与学习,clone好JDK 16源码,
打开Project Structure (command+;),设置Project SDK为JDK 16,并设置Project language level到16。

▐ 将JDK移植到Alpine Linux
更小的镜像体积分发时会更加迅速nginx
应用程序/容器的启动要迅速git
这样就能保障系统水平伸缩够快、问题出现时回滚处理够快。
另外,出于下降成本考虑,更小的镜像体积内存占用会更小,分发时耗用的资源也更小。
Alpine Linux就是与云原生的提高效率原则契合的一款独立的非商业性的通用Linux发行版。
其关注于安全性、简单性和资源效率,围绕musl libc和busybox构建。这使得它比传统的GNU/Linux发行版更小。
JDK移植到Alpine Linux后,将容许Tomcat、Jetty、Spring和其它流行的框架在其中工做。用户能够建立一个更小的镜像,以启动、运行特定的应用程序。
提早准备好Docker,咱们先构建一个Alpine Linux镜像,而后添加JDK 16,最后运行一个简单的Spring Boot程序来演示一下。
▐ 构建Alpine Linux镜像
docker pull alpinedocker run alpine echo'Hello Alpine!'
经过docker images命令查看镜像大小会发现,alpine在截止本文完成时,镜像大小仅仅只有5.6MB。相对于debian、ubuntu、centos等系统动则几十甚至上百MB的镜像来讲,alpine但是真的小!
REPOSITORY TAG IMAGE ID CREATED SIZEalpine latest 7731472c3f2a 7 weeks ago 5.61MB
▐ 添加JDK 16
OpenJDK经过使用jlink(
JEP 282:https://openjdk.java.net/jeps/282
)来减小Java运行时的大小,咱们能够从DockerHub上获取镜像:
16-jdk-alpine(https://hub.docker.com/_/openjdk?tab=tags&page=1&name=16-jdk-alpine&ordering=last_updated)
。
docker pull openjdk:16-jdk-alpine
▐ 运行Spring Boot
先准备一个Spring Boot的FatJar程序,能够从Spring Boot官网获取
Hello World!样例程序(https://spring.io/guides/gs/rest-service/)
。
建立一份Dockerfile,使用openjdk:16-jdk-alpine,并添加Spring Boot程序。
FROM openjdk:16-jdk-alpineVOLUME /tmpARG JAR_FILEADD ${JAR_FILE} app.jarENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
▐ 构建并运行
docker build --build-argJAR_FILE=target/rest-service-0.0.1-SNAPSHOT.jar -t alpine-jdk16-app:latest .docker imagesdocker run -d-p8080:8080 alpine-jdk16-app:latestdocker psdocker stop${CONTAINER_ID}curl-w'\n' http://127.0.0.1:8080/greeting?name=jdk16

至此,经过Alpine Linux系统带JDK 16运行时的Spring Boot已经启动并能够正常的访问了。
Alpine系统JDK 16镜像大小约为321MB。相比Oracle官方的Linux版本镜像的467MB,减小30%+。
记录类
从JDK 14开始提供了Record记录类的预览特性,这一特性将成为JDK 16的一项永久性特性。
Record记录类做为不可变数据的透明载体,其是为了回应有关Java过于冗长拘谨的抱怨。
此计划的目标包括设计一个表示简单值集合的面向对象的构造函数,帮助开发人员专一于对不可变数据的建模而不是扩展行为,自动实现数据驱动的方法(例如 equals() 和 属性的访问器)。

声明Record记录类后,几乎不须要添加额外的代码,一组隐式声明让其代码书写很简洁:
public record Point(int x, int y) {}
Record记录类支持Local Classes特性,那么当须要临时使用Record的时候,就能够很是方便的定义与使用:
List<Merchant>findTopMerchants(List<Merchant> merchants, int month) { record MerchantSales(Merchant merchant, double sales) {} return merchants.stream() .map(merchant ->new MerchantSales(merchant, computeSales(merchant, month))) .sorted((m1, m2) ->Double.compare(m2.sales(), m1.sales())) .map(MerchantSales::merchant) .collect(toList());}
Record记录类将能够代替Tuple、Pair等以前在JDK以外的工具库提供的元组功能,在与下面将介绍的模式匹配特性配合,可以使代码将变得很是简洁。
▐ 模式匹配
从JDK 14开始引入了一种模式匹配的预览特性,这一特性也将成为JDK 16的一项永久性特性。所以虽然JDK 16是个短时间版本,也不妨碍咱们在将来的JDK版本中继续使用模式匹配特性。
模式匹配的现阶段仅限于一种模式(类型模式)和一种语言构造(instanceof),但这只是完整特性的一部分。即使如此,咱们也已经得到了一个显著的好处:冗余的强制转换消失了,消除了冗余的代码,使更重要的代码获得了更清晰的关注,同时消除了隐藏bug的地方。
if (obj instanceofString) { String s = (String) obj; ...}
if (obj instanceofString s) { ...}
使用instanceof获取对象类型是一种条件提取形式,在得到到对象类型以后,老是要将对象强制转换为该类型。
之前在instanceof以后必须进行显式类型转换,这是一种繁琐的操做,而融合这些操做的好处不只仅是为了简洁,它还消除了一个常见的错误来源:在剪切和粘贴instanceof及强制转换代码,容易在修改了 instanceof的类型以后忘记修改强制转换类型,这就给了漏洞一个藏身之处。经过instanceof的模式匹配消除了这个问题,咱们还能够消灭全部这种类型的bug。
另外一个须要常常的作此类“先检测后强制转换”的地方是equals方法。
publicbooleanequals(Object o) { if (!(o instanceof Point)) returnfalse; Point other = (Point) o; return x == other.x && y == other.y;}
publicbooleanequals(Object o) { return (o instanceof Point other) && x == other.x && y == other.y;}
这段代码起到一样的效果,但更简单直接,由于咱们能够只使用一个复合布尔表达式来表达一个等价的条件,而不是使用控制流语句。
模式匹配的绑定变量(如上代码例子中 obj instanceof String s的s就是一个绑定变量)除了特殊的声明位置之外,其做用域也与"普通"局部变量有所不一样。
if (a instanceof Point p) { ...} else { } if (b instanceof Point p) { ...}
这样特殊的做用域让咱们可以在if-else的多分支状况下,自由的从新声明绑定变量,也考虑将来在switch中的case也是如此便利。如:
if (x instanceofInteger num) { ... }elseif (x instanceofLong num) { ... }elseif (x instanceofDouble num) { ... }
若是模式匹配能够消除Java代码中99%的强制类型转换操做,那么它确定会很流行。但还不只限于此,随着时间的推移,将会出现其余类型的模式,它们能够进行更复杂的条件提取,使用更复杂的方式来组合模式,以及提供其余可使用模式的构造:好比switch,甚至是catch,再加上目前已永久支持的Record类以及在预览中的密封类等相关特性,模式匹配将来必定可以大大简化咱们编写的代码。
尾声
本文从JDK 16版本所带来的17个新特性中抽取对工程工做和学习比较有帮助的几个特性展开解读,快速了解了这些特性。
大部分的企业或者项目还在使用JDK 8(其依然占据JDK市场的80%,绝对的主流),
源于JDK 8的超豪华新特性,如函数式接口、Lambda表达式、方法引用 / 构造器引用、更强的Steam API、接口的加强、Optional、JVM中Metaspace取代PermGen空间等等。
咱们也可以看到Java为了跟上当下技术更迭的快节奏,不断的推陈出新。
从JDK 9开始,Java版本的发布改成每6个月一次,JDK 11是长期支持版本以及下半年将发布的JDK 17。
JDK 9 模块系统、JShell交互式命令行
JDK 10 局部变量类型推断
JDK 11 ZGC试用、HTTP Client API、Steam等加强
JDK 12 switch表达式扩展、增长基于JMH的一套微基准套件
JDK 13 Socket API 重构、文本块(多行文本)
JDK 14 更有价值的NPE错误信息、JDK 16特性的部分预览
JDK 15 密封类、Record类等JDK 16特性的预览
但愿这种快速版本迭代的策略可以让Java保持持续的活力,可以让开发者使用的更高效、更健壮!
参考资料
JDK 16 的状态、发布计划与新特性
(http://openjdk.java.net/projects/jdk/16/)
JDK 16: The new features in Java 16
(https://www.infoworld.com/article/3569150/jdk-16-the-new-features-in-java-16.html)
Java源代码仓库迁移到Github
(https://www.infoworld.com/article/3569068/javas-move-to-github-set-for-september.html)
在Alpine + OpenJDK镜像中运行Spring Boot
(https://blogs.oracle.com/developers/running-spring-boot-in-a-docker-container-on-openjdk,-oracle-jdk,-zulu-on-alpine-linux,-oracle-linux,-ubuntu)
JEP 394: Pattern Matching for instanceof
(https://openjdk.java.net/jeps/394)
JEP 395: Records
(https://openjdk.java.net/jeps/395)
JEP 397: Sealed Classes (Second Preview)
(https://openjdk.java.net/jeps/397)
加入咱们
欢迎加入淘系架构团队,团队成员大牛云集,有阿里移动中间件的创始人员、Dubbo核心成员、更有一群热爱技术,指望用技术推进业务的小伙伴。
淘系架构团队,推动淘系(淘宝、天猫等)架构升级,致力于为淘系、整个集团提供基础核心能力、产品与解决方案:
业务高可用的解决方案与核心能力(精细化流量管控Marconi平台:为业务提供自适应流控、隔离与熔断的柔性高可用解决方案,站点高可用:故障自愈、多机房与异地容灾与快速切流恢复
新一代的业务研发模式FaaS(一站式函数研发Gaia平台)
下一代网络协议QUIC实现与落地
移动中间件(API网关MTop、接入层AServer、消息/推送、配置中心等等)
简历投递至📮:泽彬 zebin.xuzb@alibaba-inc.com



做者|熊政(八风)
编辑|橙子君
出品|阿里巴巴新零售淘系技术