Java 17 與 Java 11 相比有什么變化?

注】本文譯自:?What’s New Between Java11 and Java 17? - DZone Java

9 月 14 日 Java 17 發(fā)布。是時候仔細看看自上一個 LTS 版本(即 Java 11)以來的變化。我們先簡要介紹許可模型,然后重點介紹 Java 11 和 Java 17 之間的一些變化,主要是通過 例子。享受吧!
1. 介紹
首先,讓我們仔細看看 Java 許可和支持模型。Java 17 是一個 LTS(長期支持)版本,就像 Java 11 一樣。Java 11 開始了一個新的發(fā)布節(jié)奏。Java 11 支持到 2023 年 9 月,擴展支持到 2026 年 9 月。此外,在 Java 11 中,Oracle JDK 不再免費用于生產(chǎn)和商業(yè)用途。每 6 個月發(fā)布一個新的 Java 版本,即所謂的非 LTS 發(fā)布,從 Java 12 直至并包括 Java 16。但是,這些都是生產(chǎn)就緒版本。與 LTS 版本的唯一區(qū)別是支持在下一個版本發(fā)布時結束。例如。 Java 12 的支持在 Java 13 發(fā)布時結束。當您想要保持支持時,您或多或少必須升級到 Java 13。當您的某些依賴項尚未為 Java 13 做好準備時,這可能會導致一些問題。大多數(shù)情況下,對于生產(chǎn)用途,公司將等待 LTS 版本。但即便如此,一些公司也不愿意升級。最近 Snyk 的一項調(diào)查顯示,只有 60% 的人在生產(chǎn)中使用 Java 11,而這距離 Java 11 發(fā)布已經(jīng)過去了 3 年!60% 的公司仍在使用 Java 8。另一個值得注意的有趣事情是,下一個 LTS 版本將是 Java 21,它將在 2 年內(nèi)發(fā)布。關于庫在 Java 17 是否存在問題的一個很好的概述,可以在此處找到。
隨著 Java 17 的推出,Oracle 許可模式發(fā)生了變化。Java 17 是根據(jù)新的 NFTC(Oracle 免費條款和條件)許可發(fā)布的。因此,再次允許免費將 Oracle JDK 版本用于生產(chǎn)和商業(yè)用途。在同一個 Snyk 調(diào)查中,有人指出 Oracle JDK 版本在生產(chǎn)環(huán)境中僅被 23% 的用戶使用。請注意,對 LTS 版本的支持將在下一個 LTS 版本發(fā)布一年后結束??纯催@將如何影響升級到下一個 LTS 版本將會很有趣。
Java 11 和 Java 17 之間發(fā)生了什么變化?可以在 OpenJDK 網(wǎng)站上找到 JEP(Java 增強提案)的完整列表。在這里,您可以閱讀每個 JEP 的詳細信息。 有關自 Java 11 以來每個版本更改的完整列表,Oracle 發(fā)行說明提供了一個很好的概述。
在接下來的部分中,將通過示例解釋一些更改,但主要取決于您對這些新功能進行試驗以熟悉它們。這篇文章中使用的所有資源都可以在 GitHub 上找到。
最后一件事是 Oracle 發(fā)布了?Dev.java: The Destination for Java Developers,所以不要忘記看一下。
2. Text Blocks(本文塊)
為了使 Java 更具可讀性和更簡潔,已經(jīng)進行了許多改進。文本塊無疑使代碼更具可讀性。首先,我們來看看問題。假設您需要一些 JSON 字符串到您的代碼中并且您需要打印它。這段代碼有幾個問題:
雙引號的轉(zhuǎn)義;
字符串連接,使其具有或多或少的可讀性;
JSON 的復制粘貼是一項勞動密集型的工作(您的 IDE 可能會幫助您解決該問題)。
上面代碼的輸出是格式良好的 JSON。
文本塊用三個雙引號定義,其中結尾的三個雙引號不能與起始的在同一行。首先,只需打印一個空塊。為了可視化發(fā)生了什么,文本被打印在兩個雙管之間。
輸出是:
有問題的 JSON 部分現(xiàn)在可以寫成如下,這樣可讀性更好。不需要轉(zhuǎn)義雙引號,它看起來就像會被打印。
輸出當然是相同的。
在前面的輸出中,沒有前面的空格。但是,在代碼中,前面有空格。如何確定剝離前面的空格? 首先,將結尾的三個雙引號向左移動更多。
輸出現(xiàn)在在每行之前打印兩個空格。這意味著結尾的三個雙引號表示文本塊的開始。
當你將結尾的三個雙引號向右移動時會發(fā)生什么?
前面的間距現(xiàn)在由文本塊中的第一個非空格字符決定。
3. Switch 表達式
Switch 表達式將允許您從 switch 返回值并在賦值等中使用這些返回值。此處顯示了一個經(jīng)典的 switch,其中,根據(jù)給定的 Fruit 枚舉值,需要執(zhí)行一些操作。故意忽略了 break。
使用 AppLE 調(diào)用該方法。
這將打印每個 case,因為沒有 break 語句,case 就失效了。
因此,有必要在每個 case 中添加一個 break 語句,以防止這種失效。
運行此方法會為您提供所需的結果,但現(xiàn)在代碼的可讀性稍差。
這可以通過使用 Switch 表達式來解決。用箭頭 (->) 替換冒號 (:) 并確保在大小寫中使用表達式。Switch 表達式的默認行為是沒有失敗,因此不需要 break。
這已經(jīng)不那么啰嗦了,結果是相同的。
Switch 表達式也可以返回一個值。在上面的示例中,您可以返回 String 值并將它們分配給變量 text。在此之后,可以打印 text 本變量。不要忘記在最后一個案例括號后添加一個分號。
而且,更短的是,上面的內(nèi)容可以用一個語句重寫。這是否比上面的更具可讀性取決于您。
當您需要在 case 中做不止一件事情時,您會怎么做? 在這種情況下,您可以使用大括號來表示 case 塊,并在返回值時使用關鍵字 yield。
輸出現(xiàn)在有點不同,執(zhí)行了兩個打印語句。
您可以在“舊” switch 語法中使用 yield 關鍵字也很酷。這里不需要 break。
4. Records(記錄)
Records 將允許您創(chuàng)建不可變的數(shù)據(jù)類。目前,您需要例如 使用 IDE 的自動生成函數(shù)創(chuàng)建 GrapeClass 以生成構造函數(shù)、getter、hashCode、equals 和 toString,或者您可以使用 Lombok 達到同樣的目的。最后,您會得到一些樣板代碼,或者您的項目最終會依賴 Lombok。
使用上述 GrapeClass 類執(zhí)行一些測試。創(chuàng)建兩個實例,打印它們,比較它們,創(chuàng)建一個副本并也比較這個。
測試的輸出是:
GrapeRecord 具有與 GrapeClass 相同的功能,但要簡單得多。您創(chuàng)建一個記錄并指出字段應該是什么,然后您就完成了。
一個記錄可以在它自己的文件中定義,但是因為它非常緊湊,所以在需要的地方定義它也是可以的。上面用記錄重寫的測試變成如下:
輸出與上面相同。重要的是要注意記錄的副本應該以相同的副本結束。添加額外的功能,例如 grape1.nbrOfPits() 為了做一些處理并返回與初始 nbrOfPits 不同的值是一種不好的做法。雖然這是允許的,但您不應該這樣做。
構造函數(shù)可以通過一些字段驗證進行擴展。請注意,將參數(shù)分配給記錄字段發(fā)生在構造函數(shù)的末尾。
上述測試的輸出向您展示了此功能。 在構造函數(shù)內(nèi)部,字段值仍然為 null,但在打印記錄時,它們被分配了一個值。驗證也做它應該做的事情,并在顏色為 null 時拋出 IllegalArgumentException。
5. Sealed Classes(密封類)
密封類將讓您更好地控制哪些類可以擴展您的類。密封類可能更像是一個對庫所有者有用的功能。一個類在 Java 11 final 中或者可以擴展。如果您想控制哪些類可以擴展您的超類,您可以將所有類放在同一個包中,并賦予超類包可見性?,F(xiàn)在一切都在您的控制之下,但是,不再可能從包外部訪問超類。讓我們通過一個例子來看看這是如何工作的。
在包 com.mydeveloperplanet.myjava17planet.nonsealed 中創(chuàng)建一個具有公共可見性的抽象類 Fruit。在同一個包中,創(chuàng)建了最終的類 Apple 和 Pear,它們都擴展了 Fruit。
在包 com.mydeveloperplanet.myjava17planet 中創(chuàng)建一個帶有 problemSpace 方法的?SealedClasses.java?文件。如您所見,可以為 Apple、 Pear 和 Apple 創(chuàng)建實例,可以將 Apple 分配給 Fruit。除此之外,還可以創(chuàng)建一個擴展 Fruit 的 Avocado 類。
假設您不希望有人擴展 Fruit。 在這種情況下,您可以將 Fruit 的可見性更改為默認可見性(刪除 public 關鍵字)。在將 Apple 分配給 Fruit 和創(chuàng)建 Avocado 類時,上述代碼將不再編譯。后者是需要的,但我們確實希望能夠?qū)⒁粋€ Apple 分配給一個 Fruit。這可以在帶有密封類的 Java 17 中解決。
在包 com.mydeveloperplanet.myjava17planet.sealed 中,創(chuàng)建了 Fruit、Apple 和 Pear 的密封版本。唯一要做的就是將 sealed 關鍵字添加到 Fruit 類中,并使用 permits 關鍵字指示哪些類可以擴展此 Sealed 類。子類需要指明它們是 final、 sealed 還是 non-sealed。超類無法控制子類是否可以擴展以及如何擴展。
在 sealedClasses 方法中,仍然可以將 AppleSealed 分配給 FruitSealed,但 Avocado 不允許擴展 FruitSealed。 然而,允許擴展 AppleSealed 因為這個子類被指示為非密封的。
6. instanceof 的模式匹配
通常需要檢查對象是否屬于某種類型,如果是,首先要做的是將對象強制轉(zhuǎn)換為該特定類型的新變量??梢栽谝韵麓a中看到一個示例:
輸出是:
使用 instanceof 的模式匹配,上面的可以改寫如下。如您所見,可以在 instanceof 檢查中創(chuàng)建變量,并且不再需要用于創(chuàng)建新變量和轉(zhuǎn)換對象的額外行。
輸出當然與上面相同。
仔細查看變量的范圍很重要。它不應該是模棱兩可的。在下面的代碼中,&& 之后的條件只會在 instanceof 檢查結果為 true 時進行評估。 所以這是允許的。將 && 更改為 || 不會編譯。
下面的代碼顯示了另一個有關范圍的示例。如果對象不是 GrapeClass 類型,則拋出 RuntimeException。在這種情況下,永遠不會到達打印語句。在這種情況下,也可以使用 grape 變量,因為編譯器肯定知道 grape 存在。
7. 有用的空指針異常
有用的 NullPointerException 將為您節(jié)省一些寶貴的分析時間。以下代碼導致 NullPointerException。
對于 Java 11,輸出將顯示 NullPointerException 發(fā)生的行號,但您不知道哪個鏈式方法解析為 null。你必須通過調(diào)試的方式找到自己。
在 Java 17 中,相同的代碼會產(chǎn)生以下輸出,其中準確顯示了 NullPointerException 發(fā)生的位置。
8. 精簡數(shù)字格式支持
NumberFormat 中添加了一個工廠方法,以便根據(jù) Unicode 標準以緊湊的、人類可讀的形式格式化數(shù)字。 SHORT 格式樣式如下面的代碼所示:
輸出是:
LONG 格式樣式:
輸出是:
荷蘭語替換英語的 LONG 格式:
輸出是:
9. 添加了日周期支持
添加了一個新模式 B 用于格式化 DateTime,該模式根據(jù) Unicode 標準指示日期時間段。
使用默認的中文語言環(huán)境,打印一天中的幾個時刻:
輸出是:
現(xiàn)在使用荷蘭語本地環(huán)境:
輸出如下。請注意,英國之夜從 23 點開始,荷蘭之夜從 01 點開始??赡苁俏幕町?-)。
10. Stream.toList()
為了將 Stream 轉(zhuǎn)換為 List,您需要使用 collect 的 Collectors.toList() 方法。這非常冗長,如下面的示例所示。
在 Java 17 中,添加了一個 toList 方法來替換舊的行為。
11. 結論
在本文中,您快速瀏覽了自上一個 LTS 版本 Java 11 以來添加的一些功能?,F(xiàn)在由您開始考慮遷移到 Java 17 的計劃,以及了解有關這些新功能的更多信息以及您如何 可以將它們應用到您的日常編碼習慣中。提示:IntelliJ 會幫你解決這個問題!