Java9
發布於 2017 年 9 月 21 日 。作為 Java8 之后 3 年半才發布的新版本,Java 9 帶 來了很多重大的變化其中最重要的改動是 Java 平台模塊系統的引入,其他還有諸如集合、Stream 流
Java 平台模塊系統
Java 平台模塊系統,也就是 Project Jigsaw,把模塊化開發實踐引入到了 Java 平台中。在引入了模塊系統之后,JDK 被重新組織成 94 個模塊。Java 應用可以通過新增的 jlink 工具,創建出只包含所依賴的 JDK 模塊的自定義運行時鏡像。這樣可以極大的減少 Java 運行時環境的大小。
Java 9 模塊的重要特征是在其工件(artifact)的根目錄中包含了一個描述模塊的 module-info.class 文 件。 工件的格式可以是傳統的 JAR 文件或是 Java 9 新增的 JMOD 文件。
Jshell
jshell 是 Java 9 新增的一個實用工具。為 Java 提供了類似於 Python 的實時命令行交互工具。
在 Jshell 中可以直接輸入表達式並查看其執行結果
集合、Stream 和 Optional
- 增加 了
List.of()
、Set.of()
、Map.of()
和Map.ofEntries()
等工廠方法來創建不可變集合,比如List.of("Java", "C++");
、Map.of("Java", 1, "C++", 2)
;(這部分內容有點參考 Guava 的味道) Stream
中增加了新的方法ofNullable
、dropWhile
、takeWhile
和iterate
方法。Collectors
中增加了新的方法filtering
和flatMapping
Optional
類中新增了ifPresentOrElse
、or
和stream
等方法
進程 API
Java 9 增加了 ProcessHandle
接口,可以對原生進程進行管理,尤其適合於管理長時間運行的進程
平台日志 API 和服務
Java 9 允許為 JDK 和應用配置同樣的日志實現。新增了 System.LoggerFinder
用來管理 JDK 使 用的日志記錄器實現。JVM 在運行時只有一個系統范圍的 LoggerFinder
實例。
我們可以通過添加自己的 System.LoggerFinder
實現來讓 JDK 和應用使用 SLF4J 等其他日志記錄框架。
反應式流 ( Reactive Streams )
- 在 Java9 中的
java.util.concurrent.Flow
類中新增了反應式流規范的核心接口 - Flow 中包含了
Flow.Publisher
、Flow.Subscriber
、Flow.Subscription
和Flow.Processor
等 4 個核心接口。Java 9 還提供了SubmissionPublisher
作為Flow.Publisher
的一個實現。
變量句柄
- 變量句柄是一個變量或一組變量的引用,包括靜態域,非靜態域,數組元素和堆外數據結構中的組成部分等
- 變量句柄的含義類似於已有的方法句柄
MethodHandle
- 由 Java 類
java.lang.invoke.VarHandle
來表示,可以使用類java.lang.invoke.MethodHandles.Lookup
中的靜態工廠方法來創建VarHandle
對 象
改進方法句柄(Method Handle)
- 方法句柄從 Java7 開始引入,Java9 在類
java.lang.invoke.MethodHandles
中新增了更多的靜態方法來創建不同類型的方法句柄
其它新特性
- 接口私有方法 :Java 9 允許在接口中使用私有方法
- try-with-resources 增強 :在 try-with-resources 語句中可以使用 effectively-final 變量(什么是 effectively-final 變量,見這篇文章 http://ilkinulas.github.io/programming/java/2016/03/27/effectively-final-java.html)
- 類
CompletableFuture
中增加了幾個新的方法(completeAsync
,orTimeout
等) - Nashorn 引擎的增強 :Nashorn 從 Java8 開始引入的 JavaScript 引擎,Java9 對 Nashorn 做了些增強,實現了一些 ES6 的新特性
- I/O 流的新特性 :增加了新的方法來讀取和復制 InputStream 中包含的數據
- 改進應用的安全性能 :Java 9 新增了 4 個 SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 S HA3-512
- ......
Java10
發布於 2018 年 3 月 20 日,最知名的特性應該是 var 關鍵字(局部變量類型推斷)的引入了,其他還有垃圾收集器改善、GC 改進、性能提升、線程管控等一批新特性
var 關鍵字
- 介紹 :提供了 var 關鍵字聲明局部變量:
var list = new ArrayList<String>(); // ArrayList<String>
- 局限性 :只能用於帶有構造器的局部變量和 for 循環中
Guide 哥:實際上 Lombok 早就體用了一個類似的關鍵字,使用它可以簡化代碼,但是可能會降低程序的易讀性、可維護性。一般情況下,我個人都不太推薦使用。
不可變集合
list,set,map 提供了靜態方法copyOf()
返回入參集合的一個不可變拷貝(以下為 JDK 的源碼)
static <E> List<E> copyOf(Collection<? extends E> coll) {
return ImmutableCollections.listCopy(coll);
}
java.util.stream.Collectors
中新增了靜態方法,用於將流中的元素收集為不可變的集合
Optional
- 新增了
orElseThrow()
方法來在沒有值時拋出異常
並行全垃圾回收器 G1
從 Java9 開始 G1 就了默認的垃圾回收器,G1 是以一種低延時的垃圾回收器來設計的,旨在避免進行 Full GC,但是 Java9 的 G1 的 FullGC 依然是使用單線程去完成標記清除算法,這可能會導致垃圾回收期在無法回收內存的時候觸發 Full GC。
為了最大限度地減少 Full GC 造成的應用停頓的影響,從 Java10 開始,G1 的 FullGC 改為並行的標記清除算法,同時會使用與年輕代回收和混合回收相同的並行工作線程數量,從而減少了 Full GC 的發生,以帶來更好的性能提升、更大的吞吐量。
應用程序類數據共享
在 Java 5 中就已經引入了類數據共享機制 (Class Data Sharing,簡稱 CDS),允許將一組類預處理為共享歸檔文件,以便在運行時能夠進行內存映射以減少 Java 程序的啟動時間,當多個 Java 虛擬機(JVM)共享相同的歸檔文件時,還可以減少動態內存的占用量,同時減少多個虛擬機在同一個物理或虛擬的機器上運行時的資源占用
Java 10 在現有的 CDS 功能基礎上再次拓展,以允許應用類放置在共享存檔中。CDS 特性在原來的 bootstrap 類基礎之上,擴展加入了應用類的 CDS (Application Class-Data Sharing) 支持。其原理為:在啟動時記錄加載類的過程,寫入到文本文件中,再次啟動時直接讀取此啟動文本並加載。設想如果應用環境沒有大的變化,啟動速度就會得到提升
其他特性
-
線程-局部管控:Java 10 中線程管控引入 JVM 安全點的概念,將允許在不運行全局 JVM 安全點的情況下實現線程回調,由線程本身或者 JVM 線程來執行,同時保持線程處於阻塞狀態,這種方式使得停止單個線程變成可能,而不是只能啟用或停止所有線程
-
備用存儲裝置上的堆分配:Java 10 中將使得 JVM 能夠使用適用於不同類型的存儲機制的堆,在可選內存設備上進行堆內存分配
-
統一的垃圾回收接口:Java 10 中,hotspot/gc 代碼實現方面,引入一個干凈的 GC 接口,改進不同 GC 源代碼的隔離性,多個 GC 之間共享的實現細節代碼應該存在於輔助類中。統一垃圾回收接口的主要原因是:讓垃圾回收器(GC)這部分代碼更加整潔,便於新人上手開發,便於后續排查相關問題。
Java11
Java11 於 2018 年 9 月 25 日正式發布,這是很重要的一個版本!Java 11 和 2017 年 9 月份發布的 Java 9 以及 2018 年 3 月份發布的 Java 10 相比,其最大的區別就是:在長期支持(Long-Term-Support)方面,Oracle 表示會對 Java 11 提供大力支持,這一支持將會持續至 2026 年 9 月。這是據 Java 8 以后支持的首個長期版本。
字符串加強
Java 11 增加了一系列的字符串處理方法,如以下所示。
Guide 哥:說白點就是多了層封裝,JDK 開發組的人沒少看市面上常見的工具類框架啊!
//判斷字符串是否為空
" ".isBlank();//true
//去除字符串首尾空格
" Java ".strip();// "Java"
//去除字符串首部空格
" Java ".stripLeading(); // "Java "
//去除字符串尾部空格
" Java ".stripTrailing(); // " Java"
//重復字符串多少次
"Java".repeat(3); // "JavaJavaJava"
//返回由行終止符分隔的字符串集合。
"A\nB\nC".lines().count(); // 3
"A\nB\nC".lines().collect(Collectors.toList());
ZGC:可伸縮低延遲垃圾收集器
ZGC 即 Z Garbage Collector,是一個可伸縮的、低延遲的垃圾收集器。
ZGC 主要為了滿足如下目標進行設計:
- GC 停頓時間不超過 10ms
- 即能處理幾百 MB 的小堆,也能處理幾個 TB 的大堆
- 應用吞吐能力不會下降超過 15%(與 G1 回收算法相比)
- 方便在此基礎上引入新的 GC 特性和利用 colord
- 針以及 Load barriers 優化奠定基礎
- 當前只支持 Linux/x64 位平台
ZGC 目前 處在實驗階段,只支持 Linux/x64 平台
標准 HTTP Client 升級
Java 11 對 Java 9 中引入並在 Java 10 中進行了更新的 Http Client API 進行了標准化,在前兩個版本中進行孵化的同時,Http Client 幾乎被完全重寫,並且現在完全支持異步非阻塞。
並且,Java11 中,Http Client 的包名由 jdk.incubator.http
改為java.net.http
,該 API 通過 CompleteableFuture
提供非阻塞請求和響應語義。
使用起來也很簡單,如下:
var request = HttpRequest.newBuilder()
.uri(URI.create("https://javastack.cn"))
.GET()
.build();
var client = HttpClient.newHttpClient();
// 同步
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
// 異步
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
簡化啟動單個源代碼文件的方法
- 增強了 Java 啟動器,使其能夠運行單一文件的 Java 源代碼。此功能允許使用 Java 解釋器直接執行 Java 源代碼。源代碼在內存中編譯,然后由解釋器執行。唯一的約束在於所有相關的類必須定義在同一個 Java 文件中
- 對於 Java 初學者並希望嘗試簡單程序的人特別有用,並且能和 jshell 一起使用
- 一定能程度上增強了使用 Java 來寫腳本程序的能力
用於 Lambda 參數的局部變量語法
- 從 Java 10 開始,便引入了局部變量類型推斷這一關鍵特性。類型推斷允許使用關鍵字 var 作為局部變量的類型而不是實際類型,編譯器根據分配給變量的值推斷出類型
- Java 10 中對 var 關鍵字存在幾個限制
- 只能用於局部變量上
- 聲明時必須初始化
- 不能用作方法參數
- 不能在 Lambda 表達式中使用
- Java11 開始允許開發者在 Lambda 表達式中使用 var 進行參數聲明
其他特性
- 新的垃圾回收器 Epsilon,一個完全消極的 GC 實現,分配有限的內存資源,最大限度的降低內存占用和內存吞吐延遲時間
- 低開銷的 Heap Profiling:Java 11 中提供一種低開銷的 Java 堆分配采樣方法,能夠得到堆分配的 Java 對象信息,並且能夠通過 JVMTI 訪問堆信息
- TLS1.3 協議:Java 11 中包含了傳輸層安全性(TLS)1.3 規范(RFC 8446)的實現,替換了之前版本中包含的 TLS,包括 TLS 1.2,同時還改進了其他 TLS 功能,例如 OCSP 裝訂擴展(RFC 6066,RFC 6961),以及會話散列和擴展主密鑰擴展(RFC 7627),在安全性和性能方面也做了很多提升
- 飛行記錄器:飛行記錄器之前是商業版 JDK 的一項分析工具,但在 Java 11 中,其代碼被包含到公開代碼庫中,這樣所有人都能使用該功能了
Java12
增強 Switch
-
傳統的 switch 語法存在容易漏寫 break 的問題,而且從代碼整潔性層面來看,多個 break 本質也是一種重復
-
Java12 提供了 swtich 表達式,使用類似 lambda 語法條件匹配成功后的執行塊,不需要多寫 break
-
作為預覽特性加入,需要在
javac
編譯和java
運行時增加參數--enable-preview
switch (day) { case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); case TUESDAY -> System.out.println(7); case THURSDAY, SATURDAY -> System.out.println(8); case WEDNESDAY -> System.out.println(9); }
數字格式化工具類
-
NumberFormat
新增了對復雜的數字進行格式化的支持NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT); String result = fmt.format(1000); System.out.println(result); // 輸出為 1K,計算工資是多少K更方便了。。。
Shenandoah GC
- Redhat 主導開發的 Pauseless GC 實現,主要目標是 99.9% 的暫停小於 10ms,暫停與堆大小無關等
- 和 Java11 開源的 ZGC 相比(需要升級到 JDK11 才能使用),Shenandoah GC 有穩定的 JDK8u 版本,在 Java8 占據主要市場份額的今天有更大的可落地性
G1 收集器提升
- Java12 為默認的垃圾收集器 G1 帶來了兩項更新:
- 可中止的混合收集集合:JEP344 的實現,為了達到用戶提供的停頓時間目標,JEP 344 通過把要被回收的區域集(混合收集集合)拆分為強制和可選部分,使 G1 垃圾回收器能中止垃圾回收過程。 G1 可以中止可選部分的回收以達到停頓時間目標
- 及時返回未使用的已分配內存:JEP346 的實現,增強 G1 GC,以便在空閑時自動將 Java 堆內存返回給操作系統
Java13
引入 yield 關鍵字到 Switch 中
-
Switch
表達式中就多了一個關鍵字用於跳出Switch
塊的關鍵字yield
,主要用於返回一個值 -
yield
和return
的區別在於:return
會直接跳出當前循環或者方法,而yield
只會跳出當前Switch
塊,同時在使用yield
時,需要有default
條件private static String descLanguage(String name) { return switch (name) { case "Java": yield "object-oriented, platform independent and secured"; case "Ruby": yield "a programmer's best friend"; default: yield name +" is a good language"; }; }
文本塊
-
解決 Java 定義多行字符串時只能通過換行轉義或者換行連接符來變通支持的問題,引入三重雙引號來定義多行文本
-
兩個
"""
中間的任何內容都會被解釋為字符串的一部分,包括換行符String json ="{\n" + " \"name\":\"mkyong\",\n" + " \"age\":38\n" + "}\n"; // 未支持文本塊之前
String json = """ { "name":"mkyong", "age":38 } """;
增強 ZGC 釋放未使用內存
- 在 Java 11 中是實驗性的引入的 ZGC 在實際的使用中存在未能主動將未使用的內存釋放給操作系統的問題
- ZGC 堆由一組稱為 ZPages 的堆區域組成。在 GC 周期中清空 ZPages 區域時,它們將被釋放並返回到頁面緩存 ZPageCache 中,此緩存中的 ZPages 按最近最少使用(LRU)的順序,並按照大小進行組織
- 在 Java 13 中,ZGC 將向操作系統返回被標識為長時間未使用的頁面,這樣它們將可以被其他進程重用
SocketAPI 重構
- Java 13 為 Socket API 帶來了新的底層實現方法,並且在 Java 13 中是默認使用新的 Socket 實現,使其易於發現並在排除問題同時增加可維護性
動態應用程序類-數據共享
- Java 13 中對 Java 10 中引入的 應用程序類數據共享進行了進一步的簡化、改進和擴展,即:允許在 Java 應用程序執行結束時動態進行類歸檔,具體能夠被歸檔的類包括:所有已被加載,但不屬於默認基層 CDS 的應用程序類和引用類庫中的類
Java14
record 關鍵字
-
簡化數據類的定義方式,使用 record 代替 class 定義的類,只需要聲明屬性,就可以在獲得屬性的訪問方法,以及 toString,hashCode,equals 方法
-
類似於使用 Class 定義類,同時使用了 lomobok 插件,並打上了
@Getter,@ToString,@EqualsAndHashCode
注解 -
作為預覽特性引入
/** * 這個類具有兩個特征 * 1. 所有成員屬性都是final * 2. 全部方法由構造方法,和兩個成員屬性訪問器組成(共三個) * 那么這種類就很適合使用record來聲明 */ final class Rectangle implements Shape { final double length; final double width; public Rectangle(double length, double width) { this.length = length; this.width = width; } double length() { return length; } double width() { return width; } } /** * 1. 使用record聲明的類會自動擁有上面類中的三個方法 * 2. 在這基礎上還附贈了equals(),hashCode()方法以及toString()方法 * 3. toString方法中包括所有成員屬性的字符串表示形式及其名稱 */ record Rectangle(float length, float width) { }
空指針異常精准提示
-
通過 JVM 參數中添加
-XX:+ShowCodeDetailsInExceptionMessages
,可以在空指針異常中獲取更為詳細的調用信息,更快的定位和解決問題a.b.c.i = 99; // 假設這段代碼會發生空指針
Exception in thread "main" java.lang.NullPointerException: Cannot read field 'c' because 'a.b' is null. at Prog.main(Prog.java:5) // 增加參數后提示的異常中很明確的告知了哪里為空導致
switch 的增強終於轉正
- JDK12 引入的 switch(預覽特性)在 JDK14 變為正式版本,不需要增加參數來啟用,直接在 JDK14 中就能使用
- 主要是用
->
來替代以前的:
+break
;另外就是提供了 yield 來在 block 中返回值
Before Java 14
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}
Java 14 enhancements
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
instanceof 增強
-
instanceof 主要在類型強轉前探測對象的具體類型,然后執行具體的強轉
-
新版的 instanceof 可以在判斷的是否屬於具體的類型同時完成轉換
Object obj = "我是字符串";
if(obj instanceof String str){
System.out.println(str);
}
其他特性
- 從 Java11 引入的 ZGC 作為繼 G1 過后的下一代 GC 算法,從支持 Linux 平台到 Java14 開始支持 MacOS 和 Window(個人感覺是終於可以在日常開發工具中先體驗下 ZGC 的效果了,雖然其實 G1 也夠用)
- 移除了 CMS 垃圾收集器(功成而退)
- 新增了 jpackage 工具,標配將應用打成 jar 包外,還支持不同平台的特性包,比如 linux 下的
deb
和rpm
,window 平台下的msi
和exe
總結
關於預覽特性
- 先貼一段 oracle 官網原文:
This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future JDK releases. To compile and run code that contains preview features, you must specify additional command-line options.
- 這是一個預覽功能,該功能的設計,規格和實現是完整的,但不是永久性的,這意味着該功能可能以其他形式存在或在將來的 JDK 版本中根本不存在。 要編譯和運行包含預覽功能的代碼,必須指定其他命令行選項。
- 就以
switch
的增強為例子,從 Java12 中推出,到 Java13 中將繼續增強,直到 Java14 才正式轉正進入 JDK 可以放心使用,不用考慮后續 JDK 版本對其的改動或修改 - 一方面可以看出 JDK 作為標准平台在增加新特性的嚴謹態度,另一方面個人認為是對於預覽特性應該采取審慎使用的態度。特性的設計和實現容易,但是其實際價值依然需要在使用中去驗證
JVM 虛擬機優化
- 每次 Java 版本的發布都伴隨着對 JVM 虛擬機的優化,包括對現有垃圾回收算法的改進,引入新的垃圾回收算法,移除老舊的不再適用於今天的垃圾回收算法等
- 整體優化的方向是高效,低時延的垃圾回收表現
- 對於日常的應用開發者可能比較關注新的語法特性,但是從一個公司角度來說,在考慮是否升級 Java 平台時更加考慮的是JVM 運行時的提升
參考信息
- IBM Developer Java9 https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-9/
- Guide to Java10 https://www.baeldung.com/java-10-overview
- Java 10 新特性介紹https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-10/index.html
- IBM Devloper Java11 https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-11/index.html
- Java 11 – Features and Comparison: https://www.geeksforgeeks.org/java-11-features-and-comparison/
- Oracle Java12 ReleaseNote https://www.oracle.com/technetwork/java/javase/12all-relnotes-5211423.html#NewFeature
- Oracle Java13 ReleaseNote https://www.oracle.com/technetwork/java/javase/13all-relnotes-5461743.html#NewFeature
- New Java13 Features https://www.baeldung.com/java-13-new-features
- Java13 新特性概述 https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-13/index.html
- Oracle Java14 record https://docs.oracle.com/en/java/javase/14/language/records.html
- java14-features https://www.techgeeknext.com/java/java14-features