1. 程式人生 > >JDK15真的來了,一起來看看它的新特性

JDK15真的來了,一起來看看它的新特性

[toc] # 簡介 一年兩次的JDK最新版本JDK15在2020年9月15日正式釋出了,這次的JDK15給我們帶了隱藏類,EdDSA,模式匹配,Records,封閉類和Text Block等諸多新特性。 一起來看看吧。 # JDK15的新特性 ## JEP 385 Deprecate RMI Activation for Removal RMI Activation被標記為Deprecate,將會在未來的版本中刪除。 RMI大家應該都清楚,RMI就是Remote Method Invocation,翻譯成中文就是遠端方法呼叫,是在JDK1.2中引入的。 RMI為java提供了開發分散式系統的強大能力。而J2EE的規範EJB就是使用RMI來實現的bean的遠端呼叫的。 在RMI系統中,遠端系統中存在很多分散式物件,如果這些分散式物件一直處於活動狀態的話,將會佔用很多寶貴的系統資源。 於是RMI引入了一種lazy Activation的方式,這種方式就叫做延遲啟用。 這裡有兩個概念,活動物件和被動物件。 活動物件是在某些系統上的JVM中例項化並對外暴露的遠端物件。被動物件是尚未在JVM中例項化(或暴露)但可以進入主動狀態的物件。 將被動物件轉換為主動物件的過程稱為啟用。啟用要求物件與JVM關聯,這可能會將該物件的類載入到JVM中,並且將該物件恢復為之前的狀態。 在RMI系統中,我們使用延遲啟用。延遲啟用將啟用物件推遲到客戶第一次使用(即第一次方法呼叫)之前。 既然RMI Activation這麼好用,為什麼要廢棄呢? 因為對於現代應用程式來說,分散式系統大部分都是基於Web的,web伺服器已經解決了穿越防火牆,過濾請求,身份驗證和安全性的問題,並且也提供了很多延遲載入的技術。 所以在現代應用程式中,RMI Activation已經很少被使用到了。並且在各種開源的程式碼庫中,也基本上找不到RMI Activation的使用程式碼了。 為了減少RMI Activation的維護成本,在JDK8中,RMI Activation被置為可選的。現在在JDK15中,終於可以廢棄了。 ## JEP 371 Hidden Classes Hidden Classes是什麼呢? Hidden Classes就是不能直接被其他class的二淨值程式碼使用的class。Hidden Classes主要被一些框架用來生成執行時類,但是這些類不是被用來直接使用的,而是通過反射機制來呼叫。 通常來說基於JVM的很多語言都有動態生成類的機制,這樣可以提高語言的靈活性和效率。 比如在JDK8中引入的lambda表示式,JVM並不會在編譯的時候將lambda表示式轉換成為專門的類,而是在執行時將相應的位元組碼動態生成相應的類物件。 另外使用動態代理也可以為某些類生成新的動態類。 那麼我們希望這些動態生成的類需要具有什麼特性呢? 1. 不可發現性。因為我們是為某些靜態的類動態生成的動態類,所以我們希望把這個動態生成的類看做是靜態類的一部分。所以我們不希望除了該靜態類之外的其他機制發現。 2. 訪問控制。我們希望在訪問控制靜態類的同時,也能控制到動態生成的類。 3. 生命週期。動態生成類的生命週期一般都比較短,我們並不需要將其儲存和靜態類的生命週期一致。 但是現有的類的定義API ClassLoader::defineClass和Lookup::defineClass是不管類的位元組碼是如何生成的,他們都是平等對待。 所以我們需要一些API來定義無法發現的且具有有限生命週期的隱藏類。這將提高所有基於JVM的語言實現的效率。 比如: java.lang.reflect.Proxy可以定義隱藏類作為實現代理介面的代理類。 java.lang.invoke.StringConcatFactory可以生成隱藏類來儲存常量連線方法; java.lang.invoke.LambdaMetaFactory可以生成隱藏的nestmate類,以容納訪問封閉變數的lambda主體; JavaScript引擎可以為從JavaScript程式轉換的位元組碼生成隱藏的類,因為當引擎不再使用它們時,這些類將被解除安裝。 普通類是通過呼叫ClassLoader::defineClass建立的,而隱藏類是通過呼叫Lookup::defineHiddenClass建立的。 這使JVM從提供的位元組中派生一個隱藏類,連結該隱藏類,並返回提供對隱藏類的反射訪問的查詢物件。 呼叫程式可以通過返回的查詢物件來獲取隱藏類的Class物件。 ## JEP 339 Edwards-Curve Digital Signature Algorithm (EdDSA) 實現了EdDSA橢圓曲線簽名演算法。 這裡就不多講橢圓曲線簽名演算法了,如果又想了解的朋友可以給我留言。 ## JEP 375 Pattern Matching for instanceof (Second Preview) Pattern Matching 就是說可以在做pattern mathching的時候,直接對該物件進行型別的轉換。 現在這個特性還是預覽版本的。 我們看一下具體的例子: ~~~java if (obj instanceof String) { String s = (String) obj; // use s } ~~~ 在Pattern Matching之前,我們使用instanceof之後,還需要對該物件進行強制型別轉換才能使用。 但是在Pattern Matching之後,我們可以這樣用: ~~~java if (obj instanceof String s) { // can use s here } else { // can't use s here } ~~~ 是不是很方便。 ## JEP 384 Records (Second Preview) Record是一種輕量級的class,可以看做是資料結構體。和scala中的case有點相似。 舉個自定義User的例子看一下Record是怎麼用的: ~~~java public record Address( String addressName, String city ) { } ~~~ ~~~java public record CustUser( String firstName, String lastName, Address address, int age ) {} ~~~ 上面我們定義了兩個類,CustUser和Address。CustUser中引用了Address。 Record和普通的類的區別就在於Record多了一個括號括起來的定義的欄位。 Record類預設是final的,裡面的欄位預設是private final的。 要想知道Record到底是怎麼工作的,我們可以使用javap來對編譯好的class檔案反編譯,執行javap CustUser,可以得到下面的結果: ~~~java 警告: 二進位制檔案CustUser包含com.flydean.records.CustUser Compiled from "CustUser.java" public final class com.flydean.records.CustUser extends java.lang.Record { public com.flydean.records.CustUser(java.lang.String, java.lang.String, com.flydean.records.Address, int); public java.lang.String toString(); public final int hashCode(); public final boolean equals(java.lang.Object); public java.lang.String firstName(); public java.lang.String lastName(); public com.flydean.records.Address address(); public int age(); } ~~~ 上面可以看到final class CustUser繼承自java.lang.Record。 並且自動添加了預設帶有所有欄位的建構函式。各個自動的獲取方法,並實現了toString,hashCode和equals方法。 天啦,太完美了,我們想要的它居然都有。 如果上面的javap還不是很清楚的話,大家可以藉助IDE的反編譯功能,開啟CustUser.class檔案看一看: ~~~java public final class CustUser extends java.lang.Record { private final java.lang.String firstName; private final java.lang.String lastName; private final com.flydean.records.Address address; private final int age; public CustUser(java.lang.String firstName, java.lang.String lastName, com.flydean.records.Address address, int age) { /* compiled code */ } public 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 firstName() { /* compiled code */ } public java.lang.String lastName() { /* compiled code */ } public com.flydean.records.Address address() { /* compiled code */ } public int age() { /* compiled code */ } } ~~~ > 注意,上面的反編譯我們可以看到,record中的所有欄位都是final的,只能在初始化的時候設定。並且方法裡面也沒有提供其他可以改變欄位內容的方法。 所以我們得出了一個震世驚俗的結論:record是immutable的。 上面的例子中我們只使用了小括號裡面的內容,大括號還是空的呀。可不可以像其他正常的類一樣,新增點方法或者建構函式進去呢? 答案是肯定的。 先看一個整體的方案: ~~~java public record CustUserWithBody( String firstName, String lastName, Address address, int age ) { public String fullName(){ return firstName+ lastName; } public CustUserWithBody{ if (age < 18) { throw new IllegalArgumentException( "男大當婚,女大當嫁,18歲未到,不許出嫁!"); } } } ~~~ 我們在record的主題中,定義了一個方法和一個建構函式。 先看這個方法,在方法中我們可以訪問到record中定義的變數,但是千萬不要嘗試去修改他們,因為他們是final的,你會得到一個變異錯誤。 再看這個建構函式,這個建構函式沒有小括號,只有大括號,這種建構函式叫做Compact constructor。你無法在record中定義正常的建構函式,因為會得到一個編譯錯誤。 在這個Compact constructor中,我們可以對定義的欄位進行資料校驗。如上所述。 ## JEP 360 Sealed Classes (Preview) 在Java中,類層次結構通過繼承實現程式碼的重用,父類的方法可以被許多子類繼承。 但是,類層次結構的目的並不總是重用程式碼。有時,其目的是對域中存在的各種可能性進行建模,例如圖形庫支援的形狀型別或金融應用程式支援的貸款型別。 當以這種方式使用類層次結構時,我們可能需要限制子類集從而來簡化建模。 因為我們引入了sealed class或interfaces,這些class或者interfaces只允許被指定的類或者interface進行擴充套件和實現。 舉個例子: ~~~java package com.example.geometry; public abstract sealed class Shape permits Circle, Rectangle, Square {...} ~~~ 上面的例子中,我們指定了Shape只允許被Circle, Rectangle, Square來繼承。 上面的例子中並沒有指定類的包名,我們可以這樣寫: ~~~java package com.example.geometry; public abstract sealed class Shape permits com.example.polar.Circle, com.example.quad.Rectangle, com.example.quad.simple.Square {...} ~~~ ## JEP 378 Text Blocks Text Blocks是為了解決在java中輸入多行資料的問題。 比如: ~~~java String html = "\n" + " \n" + "

Hello, world

\n" + " \n" + "\n"; ~~~ 可以寫成: ~~~java String html = """

Hello, world

"""; ~~~ ~~~java String query = "SELECT \"EMP_ID\", \"LAST_NAME\" FROM \"EMPLOYEE_TB\"\n" + "WHERE \"CITY\" = 'INDIANAPOLIS'\n" + "ORDER BY \"EMP_ID\", \"LAST_NAME\";\n"; ~~~ 可以寫成: ~~~java String query = """ SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB" WHERE "CITY" = 'INDIANAPOLIS' ORDER BY "EMP_ID", "LAST_NAME"; ~~~ 非常的方便。 # 總結 好了,JDK15的新特性全都介紹完了。希望大家能夠喜歡。 >
本文作者:flydean程式那些事 > > 本文連結:[http://www.flydean.com/jdk15-release-new-features/](http://www.flydean.com/jdk15-release-new-features/) > > 本文來源:flydean的部落格 > > 歡迎關注我的公眾號:「程式那些事」最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!