JDK14最新版本中的新增80種新功能和API - Azul

banq發表於2020-03-20

JDK14總共有非常令人印象深刻的16個JDK增強建議(JEP)和69個新的API元素。

讓我們從介紹Java語言語法更改的更重要的專案開始。

記錄

Java是一種物件導向的語言。您可以建立類來儲存資料,並使用封裝來控制如何訪問和修改該資料。物件的使用使操作複雜的資料型別變得簡單而直接。這是Java如此流行作為平臺的原因之一。

缺點(到現在為止)是,建立資料型別非常冗長,即使在最直接的情況下也需要大量程式碼。讓我們看一下基本二維點所需的程式碼:

public class Point {
  private final double x;
  private final double y;
  public Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
  public double getX() {
    return x;
  }
  public double getY() {
    return y;
  }
}

JDK 14引入了記錄作為預覽功能。預覽功能是一個新概念,使Java平臺的開發人員可以在不使其成為Java SE標準一部分的情況下包括新的語言功能。通過這樣做,開發人員可以嘗試這些功能,並提供反饋,以便在必要時在標準中設定功能之前進行更改(甚至刪除功能)。要使用預覽功能,必須為編譯和執行時指定命令列標誌--enable-preview。對於編譯,還必須指定-source標誌。

記錄是表示資料類的一種簡單得多的方法。如果以Point為例,程式碼可以簡化為一行:

public record Point(double x, double y) { }

這與程式碼的可讀性無關。我們立即意識到,我們現在有了一個包含兩個稱為x和y的雙精度值的類,我們可以使用getX和getY的標準訪問器方法名稱進行訪問。

讓我們檢查一下記錄的一些細節。

首先,記錄是一種新的型別,它是類的受限形式,就像列舉一樣。記錄具有名稱和狀態描述,用於定義記錄的組成部分。在上面的Point示例中,狀態描述是double和x和y。記錄是為了簡化而設計的,因此它們不能擴充套件任何其他類或定義其他例項變數。記錄中的所有狀態都是最終狀態,因此不提供訪問器(設定器)方法。如果您需要任何一個,則需要使用成熟的類。

記錄確實具有靈活性。

通常,建構函式除了提供值外還需要提供其他行為。如果是這種情況,我們可以提供建構函式的替代實現:

record Range(int min, int max) {
  Range {
    if (min > max)
      throw new IllegalArgumentException(“Max must be >= min”);
  }
}

模式匹配instanceof

在某些情況下,您不知道物件的確切型別。為了解決這個問題,Java有instanceof運算子,可用於針對不同型別進行測試。這樣做的缺點是確定了物件的型別。如果要使用顯式型別轉換,則必須使用顯式型別轉換:

if (o instanceof String) {
  String s = (String)o;
  System.out.println(s.length);
}

在JDK 14中,對instanceof運算子進行了擴充套件,以允許除了型別之外還指定變數名。然後可以使用該變數而無需顯式強制轉換:

if (o instanceof String s)
  System.out.println(s.length);

變數的範圍限於在邏輯上正確使用的地方,因此:

if (o instanceof String s)
  System.out.println(s.length);
else
  // s is out of scope here

作用域在也可以在條件語句中應用,因此我們可以執行以下操作:

if (o instanceof String s && s.length() > 4) ...

在下面情況下,無論o是否為String的結果,都需要對s.length()進行求值。從邏輯上講,這不起作用,因此會導致編譯錯誤。

if (o instanceof String s || s.length() > 4) ...

使用邏輯非運算子可以產生一些有趣的作用域效果:

if (!(o instanceof String s && s.length() > 3)
  return;
System.out.println(s.length());  // s is in scope here

我已經看到了對變數的作用域劃分方式的一些負面反饋,但是,鑑於所有作用域都是完全合乎邏輯的,因此我認為它非常有效。

已經有計劃提供此功能的第二個預覽版本,該版本將擴充套件模式匹配以與記錄一起使用,並提供實現解構模式的簡單方法。可以在JEP 375中找到更多詳細資訊。

有用的NullPointerException

任何編寫了多行Java程式碼的人都將在某個時候遇到NullPointerException。未能初始化物件引用(或將其錯誤地顯式設定為null),然後嘗試使用該引用將導致引發此異常。

在簡單的情況下,找出問題的原因很簡單。如果我們嘗試執行以下程式碼:

public class NullTest {
  List<String> list;
  public NullTest() {
    list.add("foo");
  }
}

錯誤:

Exception in thread "main" java.lang.NullPointerException
            at jdk14.NullTest.<init>(NullTest.java:16)
            at jdk14.Main.main(Main.java:15)

在JDK 14中,如果我們執行相同的程式碼,我們將看到類似以下內容:

Exception in thread "main" java.lang.NullPointerException:
    Cannot read field "c" because "a.b" is null
    at Prog.main(Prog.java:5)

馬上我們可以看到ab是問題所在,並著手進行糾正。我敢肯定,這將使許多Java開發人員的生活更加輕鬆。

新的API

現在,讓我們將注意力轉向類庫中的更改。

java.io

PrintStream有兩個新方法,write(byte [] buf)和writeBytes(byte [] buf)。這些有效地做同樣的事情,等效於write(buf,0,buf.length)。之所以擁有兩種不同的方法,是因為將write定義為丟擲IOException(但是,奇怪的是,從不丟擲),而writeBytes沒有。因此,選擇使用哪種方法取決於您是否希望使用try-catch塊包圍該呼叫。

有一個新的註釋型別,序列。它旨在用於對序列化的編譯器檢查。特別是,此型別的註釋應應用於與序列化相關的方法和宣告為可序列化的類中的欄位。(它在某些方面與Override註釋類似)。

java.lang

Class類為新的Record功能提供了兩種方法:isRecord()和getRecordComponents()。getRecordComponents()方法返回一個RecordComponent物件陣列。RecordComponent是java.lang.reflect包中的新類,它具有11種方法來檢索內容,例如註釋的詳細資訊和泛型。

Record是一個簡單的新類,它重寫Object的equals,hashCode和toString方法。

NullPointerException現在作為有用的NullPointerExceptions功能的一部分覆蓋了Throwable中的getMessage方法。

StrictMath類具有六個新方法,這些方法補充了需要檢測溢位錯誤時使用的現有精確方法。新方法是decrementExact,incrementExact和negateExact(所有方法都有int和long引數的兩個過載版本)。

java.lang.annotation

ElementType列舉為Records新增了一個新的常量RECORD_TYPE。

java.lang.invoke

MethodHandles.Lookup類具有兩個新方法:

  • hasFullPrivilegeAccess,如果要查詢的方法同時具有PRIVATE和MODULE訪問權,則返回true。
  • previousLookupClass,它報告此查詢物件先前從其傳送過來的另一個模組中的查詢類,或者為null。以前,我從未聽說過在Java上下文中進行隱形傳送(僅在Doctor Who和Minecraft中)。查詢可能導致模組之間的傳送。

java.lang.runtime

這是JDK 14中的新軟體包,只有一個類ObjectMethods。這是記錄功能的低階部分,它具有單個方法(載入程式),該方法生成物件等於,hashCode和toString方法。

java.util.text

CompactNumberFormat類具有一個新的建構函式,該建構函式為pluralRules新增了一個引數。這是一個String,表示將Count關鍵字(例如“ one”)與實際整數關聯的多個規則。它的語法在Unicode Consortium的複數規則語法中定義。

java.util

HashSet類具有一個新方法toArray,該方法返回一個陣列,其執行時元件型別為Object,包含此集合中的所有元素。

java.util.concurrent.locks

LockSupport類具有一個新方法setCurrentBlocker。LockSupport提供了暫存和取消暫存執行緒的功能(與不贊成使用的Thread.suspend和Thread.resume方法沒有相同的問題)。現在可以設定getBlocker將返回的Object。從非公共物件呼叫無引數停放方法時,這很有用。

javax.lang.model.element

ElementKind列舉具有三個新常量,用於記錄和模式匹配例項的特徵,即BINDING_VARIABLE,RECORD和RECORD_COMPONENT。

javax.lang.model.util

該軟體包提供實用程式,以協助處理程式元素和型別。通過新增記錄,新增了一組新的抽象類和具體類來支援此功能。(示例是AbstractTypeVisitor14,ElementScanner14和TypeKindVisitor14)。

org.xml.sax

一種新方法已新增到SAX XML解析器的ContentHandler介面。宣告方法接收XML宣告的通知。在預設實現的情況下,此操作無效。

JEP 370:外部記憶體訪問API

這是作為孵化器模組引入的,以允許更廣泛的Java社群進行測試,並在其成為Java SE標準的一部分之前整合反饋。它旨在替代sun.misc.Unsafe和java.io.MappedByteBuffer。

外部儲存器訪問API引入了三個主要抽象:

  • MemorySegment:提供對具有給定範圍的連續記憶體區域的訪問。
  • MemoryAddress:提供到MemorySegment的偏移量(基本上是一個指標)。
  • MemoryLayout:提供一種描述記憶體段佈局的方法,該方法大大簡化了使用var控制程式碼訪問MemorySegment的過程。使用此功能,無需根據記憶體的使用方式來計算偏移量。例如,一個整數或長整數陣列的偏移量將有所不同,但將使用MemoryLayout透明地對其進行處理。

JVM更改

我掃描了JVM規範的所有609頁,但是找不到突出顯示的差異。檢視為記錄生成的位元組碼會很有趣,因為在JVM級別上不需要任何特殊支援。有用的NullPointerException功能也是如此。

JDK 14確實包含一些JEP,這些JEP更改了JVM的非功能部分

  • JEP 345:G1的NUMA感知記憶體分配。這樣可以提高使用非均勻記憶體體系結構(NUMA)的大型計算機的效能。
  • JEP 363:刪除併發標記掃描(CMS)垃圾收集器。從JDK 9開始,G1已經成為預設的收集器,大多數(但不是全部)都認為G1是CMS的高階收集器。考慮到維護兩個相似的配置檔案收集器所需的資源,Oracle決定棄用CMS(在JDK 9中也是如此),現在將其刪除。如果您正在尋找一款高效能,低延遲替代,為什麼不嘗試 C4誠JVM
  • JEP 349:Java飛行記錄器事件流。通過啟用工具以非同步方式訂閱Java Flight Recorder事件,這可以對JVM進行更實時的監視。
  • JEP 364:macOS上的ZGC和 JEP 365:Windows上的ZGC。ZGC是實驗性的低延遲收集器,最初僅在Linux上受支援。現在,它已擴充套件到macOS和Windows作業系統。
  • JEP 366:棄用ParallelScavenge和SerialOld GC組合。Oracle指出,很少有人會使用這種組合,並且維護開銷相當大。期望在不久的將來某個時候可以刪除此組合。

其它功能

有許多與OpenJDK的不同部分相關的JEP:

  • JEP 343:打包工具。這是一個簡單的打包工具,基於從JDK 11中的Oracle JDK中刪除的JavaFX javapackager工具。它有很多功能,因此我將在單獨的部落格文章中詳細介紹。
  • JEP 352:非易失性對映位元組緩衝區。這新增了新的特定於JDK的檔案對映模式,以便可以使用FileChannel API建立引用非易失性儲存器的MappedByteBuffer例項。新增了一個新模組jdk.nio.mapmode,以允許將MapMode設定為READ_ONLY_SYNC或WRITE_ONLY_SYNC。
  • JEP 361:開關表示式標準。開關表示式是JDK 12中新增到OpenJDK的第一個預覽功能。在JDK 13中,反饋導致將break值語法更改為yield 值。 在JDK 14中,開關表示式不再是預覽功能,並且已包含在Java SE標準中。
  • JEP 362:棄用Solaris和SPARC埠。由於Oracle不再開發Solaris作業系統或SPARC晶片體系結構,因此他們不再需要繼續維護這些埠。這些可能會被Java社群中的其他人所接受。
  • JEP 367:刪除Pack 200工具和API。JDK 11不推薦使用的另一個功能現在已從JDK 14中刪除。此壓縮格式的主要用途是用於applet使用的jar檔案。鑑於瀏覽器外掛已從Oracle JDK 11中刪除,這似乎是一個合理的功能。
  • JEP 368:文字塊。這些已作為預覽功能包含在JDK 13中,並且在JDK 14中繼續進行第二次迭代(仍在預覽中)。文字塊提供了對多行字串文字的支援。所做的更改是新增了兩個新的轉義序列。第一個通過在行的末尾新增\來抑制換行符的包含(這在軟體開發的許多其他地方很常見)。第二個是\ s,表示單個空格。這對於防止從文字塊中的行尾剝離空白很有用。

如您所見,JDK 14包含了許多新功能,這些功能將使開發人員的生活更加輕鬆。

現在可以免費下載OpenJDK 14的免費Zulu社群版本。為什麼不試試呢?

 

相關文章