Java 13 新特性全面解讀

Java 13 新特性全面解讀作者 l Hollis 本文經授權轉載自 Hollis (ID:hollischuang)

2017 年 8 月,JCP 執行委員會提出將 Java 的發佈頻率改爲每六個月一次,新的發佈週期嚴格遵循時間點,將在每年的 3 月份和 9 月份發佈。

Java 13 新特性全面解讀

目前該版本包含的特性已經全部固定,主要包含以下五個:

JEP 350,Dynamic CDS Archives

JEP 351,ZGC: Uncommit Unused Memory

JEP 353,Reimplement the Legacy Socket API

JEP 354: Switch Expressions (Preview)

JEP 355,Text Blocks (Preview)

下面來逐一介紹下這五個重要的特性。

Java 13 新特性全面解讀

Dynamic CDS Archives

這一特性是在 JEP310:Application Class-Data Sharing 基礎上擴展而來的,Dynamic CDS Archives 中的 CDS 指的就是 Class-Data Sharing。

那麼,這個 JEP310 是個啥東西呢?

我們知道在同一個物理機/虛擬機上啓動多個 JVM 時,如果每個虛擬機都單獨裝載自己需要的所有類,啓動成本和內存佔用是比較高的。所以 Java 團隊引入了 CDS 的概念,通過把一些核心類在每個 JVM 間共享,每個 JVM 只需要裝載自己的應用類,啓動時間減少了,另外核心類是共享的,所以 JVM 的內存佔用也減少了。

CDS 只能作用於 Boot Class Loader 加載的類,不能作用於 App Class Loader 或者自定義的 Class Loader 加載的類。

在 Java 10 中,則將 CDS 擴展爲 AppCDS,顧名思義,AppCDS 不止能夠作用於 Boot Class Loader 了,App Class Loader 和自定義的 Class Loader 也都能夠起作用,大大加大了 CDS 的適用範圍。也就說開發自定義的類也可以裝載給多個 JVM 共享了。

Java 10 中包含的 JEP310 的通過跨不同 Java 進程共享公共類元數據來減少了內存佔用和改進了啓動時間。

但是,JEP310 中,使用 AppCDS 的過程還是比較複雜的,需要有三個步驟:

1、決定要 Dump 哪些 Class

2、將類的內存 Dump 到歸檔文件中

3、使用 Dump 出來的歸檔文件加快應用啓動速度

這一次的 JDK 13 中的 JEP 350 ,在 JEP310 的基礎上,又做了一些擴展。允許在 Java 應用程序執行結束時動態歸檔類,歸檔類將包括默認的基礎層 CDS (class data-sharing)存檔中不存在的所有已加載的應用程序類和庫類。

也就是說,在 Java 13 中再使用 AppCDS 的時候,就不在需要這麼複雜了。

Java 13 新特性全面解讀

ZGC: Uncommit Unused Memory

在討論這個問題之前,想先問一個問題,JVM 的 GC 釋放的內存會還給操作系統嗎?

GC 後的內存如何處置,其實是取決於不同的垃圾回收器的。因爲把內存還給 OS,意味着要調整 JVM 的堆大小,這個過程是比較耗費資源的。

在 JDK 11 中,Java 引入了 ZGC,這是一款可伸縮的低延遲垃圾收集器,但是當時只是實驗性的。並且,ZGC 釋放的內存是不會還給操作系統的。

Java 13 新特性全面解讀

而在 Java 13 中,JEP 351 再次對 ZGC 做了增強,本次 ZGC 可以將未使用的堆內存返回給操作系統。之所以引入這個特性,是因爲如今有很多場景中內存是比較昂貴的資源,在以下情況中,將內存還給操作系統還是很有必要的:

  • 1、那些需要根據使用量付費的容器

  • 2、應用程序可能長時間處於空閒狀態並與許多其他應用程序共享或競爭資源的環境。

  • 3、應用程序在執行期間可能有非常不同的堆空間需求。例如,啓動期間所需的堆可能大於稍後在穩定狀態執行期間所需的堆。

Java 13 新特性全面解讀

Reimplement the Legacy Socket API

使用易於維護和調試的更簡單、更現代的實現替換 java.net.Socket 和 java.net.ServerSocket API。

java.net.Socket 和 java.net.ServerSocket 的實現非常古老,這個 JEP 爲它們引入了一個現代的實現。現代實現是 Java 13 中的默認實現,但是舊的實現還沒有刪除,可以通過設置系統屬性 jdk.net.usePlainSocketImpl 來使用它們。

運行一個實例化 Socket 和 ServerSocket 的類將顯示這個調試輸出。這是默認的 (新的):
[code]
java -XX:+TraceClassLoading JEP353 | grep Socket
[0.033s][info ][class,load] java.net.Socket source: jrt:/java.base
[0.035s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.035s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
[0.042s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.042s][info ][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base
[0.043s][info ][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base
[0.044s][info ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.045s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.045s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
[/code]
[code]
上面輸出的 sun.nio.ch.NioSocketImpl 就是新提供的實現。
[/code]

如果使用舊的實現也是可以的(指定參數 jdk.net.usePlainSocketImpl):
[code]
$ java -Djdk.net.usePlainSocketImpl -XX:+TraceClassLoading JEP353 | grep Socket
[0.037s][info ][class,load] java.net.Socket source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.043s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
[0.046s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.PlainSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base
[0.047s][info ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
[0.047s][info ][class,load] java.net.SocketOption source: jrt:/java.base
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
[0.048s][info ][class,load] jdk.net.LinuxSocketOptions source: jrt:/jdk.net
[0.048s][info ][class,load] jdk.net.LinuxSocketOptions$$Lambda$2/0x0000000800b51040 source: jdk.net.LinuxSocketOptions
[0.049s][info ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
[0.049s][info ][class,load] java.net.StandardSocketOptions source: jrt:/java.base
[0.049s][info ][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base
[0.051s][info ][class,load] sun.net.ext.ExtendedSocketOptions$$Lambda$3/0x0000000800b51440 source: sun.net.ext.ExtendedSocketOptions
[0.057s][info ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
[0.057s][info ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
[0.058s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.058s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.058s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
[/code]

上面的結果中,舊的實現 java.net.PlainSocketImpl 被用到了。

Java 13 新特性全面解讀

Switch Expressions (Preview)

在 JDK 12 中引入了 Switch 表達式作爲預覽特性。JEP 354 修改了這個特性,它引入了 yield 語句,用於返回值。這意味着,switch 表達式 (返回值) 應該使用 yield, switch 語句 (不返回值) 應該使用 break。

在以前,我們想要在 switch 中返回內容,還是比較麻煩的,一般語法如下:
[code]
int i;
switch (x) {
case "1":
i=1;
break;
case "2":
i=2;
break;
default:
i = x.length();
break;
}
[/code]
[/code]

在 JDK13 中使用以下語法:
[code]
int i = switch (x) {
case "1" -> 1;
case "2" -> 2;
default -> {
int len = args[1].length();
yield len;
}
};
[/code]
[/code]

或者
[code]
int i = switch (x) {
case "1": yield 1;
case "2": yield 2;
default: {
int len = args[1].length();
yield len;
}
};
[/code]
[/code]

在這之後,switch 中就多了一個關鍵字用於跳出 switch 塊了,那就是 yield,他用於返回一個值。和 return 的區別在於:return 會直接跳出當前循環或者方法,而 yield 只會跳出當前 switch 塊。

Java 13 新特性全面解讀

Text Blocks (Preview)

在 JDK 12 中引入了 Raw String Literals 特性,但在發佈之前就放棄了。這個 JEP 在引入多行字符串文字(text block)在意義上是類似的。

text block,文本塊,是一個多行字符串文字,它避免了對大多數轉義序列的需要,以可預測的方式自動格式化字符串,並在需要時讓開發人員控制格式。

我們以前從外部 copy 一段文本串到 Java 中,會被自動轉義,如有一段以下字符串:
[code]


Hello, Hollisp>
body>
html>
[/code]
[/code]

將其複製到 Java 的字符串中,會展示成以下內容:
[code]
"\n" +
" \n" +
"

Hello, Hollisp>\n" +
" body>\n" +
"html>\n";
[/code]
[/code]

即被自動進行了轉義,這樣的字符串看起來不是很直觀,在 JDK 13 中,就可以使用以下語法了:
[code]
"""

Hello, Hollis




""";

[/code]
[/code]

使用 """ 作爲文本塊的開始符合結束符,在其中就可以放置多行的字符串,不需要進行任何轉義。看起來就十分清爽了。

如常見的 SQL 語句:
[code]
String query = """
SELECT EMP_ID, LAST_NAME FROM EMPLOYEE_TB
WHERE CITY = 'INDIANAPOLIS'
ORDER BY EMP_ID, LAST_NAME;
""";
[/code]
[/code]

看起來就比較直觀,清爽了。

Java 13 新特性全面解讀

總結

以上,就是 JDK13 中包含的 5 個特性,能夠改變開發者的編碼風格的主要有 Text Blocks 和 Switch Expressions 兩個新特性,但是這兩個特性還處於預覽階段。

而且,JDK13 並不是 LTS (長期支持)版本,如果你正在使用 Java 8 (LTS)或者 Java 11 (LTS),暫時可以不必升級到 Java 13。

Java 13 新特性全面解讀參考資料: https://openjdk.java.net/projects/jdk/13/ https://metebalci.com/blog/what-is-new-in-java-13/https://www.jianshu.com/p/890196bf529a

作者簡介:Hollis,知名技術博主,個人博客文章閱讀量數百萬,CSDN 博客專家。工作地點杭州,主要從事金融,支付領域的 Java 開發,熱衷於技術分享。

本文轉載自 Hollis 公衆號,專注原創技術文章,主要以 Java 相關技術爲主,覆蓋基礎知識、底層原理、技術成長等話題。

【END】

Java 13 新特性全面解讀

熱 文 推 薦

榮耀趙明 “Diss” 5G 手機;甲骨文創始人埃裏森:Uber 一文不值;Chrome 77 發佈 | 極客頭條
華爲此時就把 5G 芯片用在手機上,壘起了多高的競爭壁壘?
☞海闊憑鯤躍 天高任鵬飛——從一場大賽看華爲雲的鯤鵬之“智”☞我願出 2 倍工資,挖這個被裁的程序員!☞阿里雲智能運維的自動化三劍客免費開源!新學期必收藏的 AI 學習資源,從課件、工具到源碼都齊了華爲 | 泰山之巔 鯤鵬展翅 扶搖直上九萬里
鴻蒙 OS 的到來,能爲我們改變什麼?| 程序員大本營 9 月刊厲害!接班馬雲的爲何是張勇?Java 13 新特性全面解讀你點的每個“在看”,我都認真當成了喜歡

來源鏈接:mp.weixin.qq.com