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
做者|熊政(八風)
編輯|橙子君
出品|阿里巴巴新零售淘系技術