傳送門
JDK8新特性
JDK9新特性
JDK10新特性
JDK11新特性
JDK12新特性
JDK13新特性
JDK14新特性
JDK15新特性
JDK16新特性
JDK17新特性
JDK18新特性
JDK19新特性
JDK20新特性
JDK21新特性
前言
JDK 21 于 2023 年 9 月 19 日 發(fā)布,這是一個非常重要的版本,里程碑式。
JDK21 是 LTS(長期支持版),至此為止,目前有 JDK8、JDK11、JDK17 和 JDK21 這四個長期支持版了。
JDK個版本含義:
GA,就是我上面框起來的“General Availability”的縮寫,直譯成中文,雖然是“普通可用”的意思,但是在軟件行業(yè),它就代表正式版。
如果對外發(fā)布一個 GA 版本,就意味著這個版本已經(jīng)經(jīng)過全面的測試,不存在任何重大的 bug,可供普通用戶進行使用。
既然說到 GA 了,也順便給大家普及一下一般我們看到的版本號的含義。
比如我們經(jīng)常會看到一些軟件發(fā)布的時候都會帶上 Alpha、Beta、Gamma、RC 等等這些莫名其妙的單詞,它們代表什么意思呢?
Alpha:軟件或系統(tǒng)的內(nèi)部測試版本,僅內(nèi)部人員使用。一般不向外部發(fā)布,通常會有很多 Bug,除非你也是測試人員,否則不建議使用,alpha 就是 α,是希臘字母的第一位,表示最初級的版本,beta 就是 β,alpha 版就是比 beta 還早的測試版,一般都是內(nèi)部測試的版本。
Beta:公開測試版。β 是希臘字母的第二個,顧名思義,這一版本通常是在 Alpha 版本后,該版本相對于 Alpha 版已有了很大的改進,消除了嚴重的錯誤,但還是存在著一缺陷,需要經(jīng)過多次測試來進一步消除。這個階段的版本會一直加入新的功能。
Gamma:軟件或系統(tǒng)接近于成熟的版本,只需要做一些小的改進就能發(fā)行。是 beta 版做過一些修改,成為正式發(fā)布的候選版本。
RC:Release Candidate,發(fā)行候選版本。和 Beta 版最大的差別在于 Beta 階段會一直加入新的功能,但是到了 RC 版本,幾乎就不會加入新的功能了,而主要著重于除錯。RC 版本是最終發(fā)放給用戶的最接近正式版的版本,發(fā)行后改正 bug 就是正式版了,就是正式版之前的最后一個測試版。
GA:General Available,正式發(fā)布的版本,這個版本就是正式的版本。在國外都是用 GA 來說明 release 版本的。比如:MySQL Community Server 5.7.21 GA 這是 MySQL Community Server 5.7 第 21 個發(fā)行穩(wěn)定的版本,GA 意味著 General Available,也就是官方開始推薦廣泛使用了。
Release:這個版本通常就是所謂的“最終版本”,在前面版本的一系列測試版之后,終歸會有一個正式版本,是最終交付用戶使用的一個版本,該版本有時也稱為標準版。一般情況下,Release 不會以單詞形式出現(xiàn)在軟件封面上,取而代之的是符號?。
Stable:穩(wěn)定版。在開源軟件中,都有 stable 版,這個就是開源軟件的最終發(fā)行版,用戶可以放心大膽的用了。這一版本基于 Beta 版,已知 Bug 都被修復,一般情況下,更新比較慢。
除了上面的這些之外,我們還經(jīng)常看見一個 LTS 的版本號。
LTS,Long Term Support,長期支持版,是指針對軟件的某一版本,提供長時間的技術支持、安全更新和錯誤修復。
相對于非 LTS 版本,LTS 版本被認為是更為穩(wěn)定、可靠和安全的版本。因此,在需要穩(wěn)定性和安全性較高的場景中,如生產(chǎn)環(huán)境、企業(yè)級應用等,LTS 版本得到廣泛的應用。
在 Java 領域,LTS 版本是指 Oracle 公司發(fā)布的 Java SE(Standard Edition,標準版)中,每隔一段時間發(fā)布一個長期支持版本。
自 2018 年開始,Oracle Java SE 8 、Java SE 11、Java SE 17 成為了 LTS 版本,分別提供了 3 年、 8 年、至少 3 年的支持。
你看,一個小小的 GA 里面,隱藏了這么多的小知識點,讓一不小心就鋪(水)墊(了)這么長。
JDK 21 的 GA 版本,一共發(fā)布了 15 個新特性:
并且可以看出下次LTS版本是JDK25
一、虛擬線程
圖中可以看出,Tomcat11在JDK21發(fā)布之前就預先支持虛擬線程了,非??春肑DK21。
1、Virtual Threads的開始
JDK19 預覽版初次出現(xiàn)
在從2017年loom項目正式開始,Virtual Threads的推進算是比較迅速的,JDK19出了預覽版之后,很多大佬都對此表示肯定。
相比較后面JDK21的release版本, JDK19的預覽版基本沒有什么太大的區(qū)別,無非是一些細節(jié)上的完善以及JDK內(nèi)部對Virtual Threads的應用。
JDK21 Virtual Threads的正式發(fā)布
兩年后的現(xiàn)在,千呼萬喚始出來,JDK21在23年9月19日正式發(fā)布。Virtual Threads作為正式的Feature亮相。
相對于近些年來炙手可熱的Go來說,Java算得上一個老前輩了,Java全面的功能,完整的生態(tài),無數(shù)開發(fā)者的支持,也不算弱的性能,讓Java在服務端開發(fā)上仍是TOP級別。
但是,大人,時代變了。
Go雖然也不算橫空出世,發(fā)展了10多年之后,終于在云原生時代崛起,其極佳的性能表現(xiàn)在k8s等基礎設施的服務端開放上,有著很大的優(yōu)勢。
雖然Go作為近些年崛起的語言仍然有著不太完備的表現(xiàn),像2022年才支持的泛型、Go modules、異常處理等基礎為開發(fā)人員詬病。
但是Go語言的并發(fā)處理方式,非常的輕量級,且易于使用,如果你想并發(fā)地運行一個函數(shù),只需要使用 go function(arguments)。如果你需要讓函數(shù)間進行通信,你可以使用通道,這些通道默認會同步執(zhí)行,即在兩端都準備就緒前,會暫停執(zhí)行。 goroutine、GC機制、快速的編譯時間等等強大特性,吸引了眾多的開發(fā)者在越來越多的服務端應用上使用。
對于進程和線程等簡單描述如下:
進程:
進程是應用程序的啟動實例,每個進程都有獨立的內(nèi)存空間,不同進程通過進程間的通信方式來通信。
線程:
線程從屬于進程,每個進程至少包含一個線程,線程是CPU調(diào)度的基本單位,多個線程之間可以共享進程的資源并通過共享內(nèi)存等線程間的通信方式來通信。
協(xié)程:
協(xié)程可以理解為一種輕量級線程,與線程相比,協(xié)程不受操作系統(tǒng)調(diào)度,協(xié)程調(diào)度器由用戶應用程序提供,協(xié)程調(diào)度器按照調(diào)度策略把協(xié)程調(diào)度到線程中運行。
Go語言的協(xié)程,相比于Java,其更好的性能表現(xiàn)、更輕量級、不需要操作系統(tǒng)調(diào)度讓很多Java開發(fā)者垂涎,盼星星盼月亮,也希望Java什么時候也能老樹逢春,久旱逢甘霖。
其實很早之前Oracle官方就傳出了要給JDK搞一個輕量級線程,最早很多人把它叫做纖程(fibers),聽起來就很輕很細~。
2、為什么需要Virtual Threads
Virtual Threads的出現(xiàn)解決了什么問題,相對于之前的操作系統(tǒng)線程或者說平臺線程(Platform Threads)有什么優(yōu)勢。
JVM使用平臺線程和操作系統(tǒng)線程是一一對應的,有些使用的缺點在Java不斷地發(fā)展中不斷地凸顯出來
- 創(chuàng)建單個線程所需資源過多,平臺線程本身就是很珍貴的資源
- 受限于機器硬件,平臺線程的個數(shù)不能創(chuàng)建過多,且創(chuàng)建及回收線程需要耗費一定資源
我們不可能為每個請求或者說任務分配一個單獨的線程,當請求量達到一定閾值之后,只能通過線程池或者其他異步的方式來處理請求。 - Java運行中線程上下文的切換非常頻繁,切換需要的資源也耗費不少
3、那么Virtual Threads是如何應對這些問題
- Virtual Threads的關鍵特點是便宜、數(shù)量多、輕量級,相對于OS線程代價可能只是千分之一。
- 無需池化使用,壽命周期短
- 每個請求都創(chuàng)建一個虛擬線程,無需上下文切換 thread-per-request style
- 由OS線程裝載Virtual Threads,并隨時可以卸載(特殊同步代碼塊除外),然后執(zhí)行其他Virtual Threads
首先需要明確的是Virtual Threads是基于平臺線程(OS線程)運行的,并且是由JVM創(chuàng)建并管理的。
它是由JVM調(diào)度,mounting (裝載)到OS線程上執(zhí)行的,當阻塞的時候,再從OS線程上unmounting(卸載),OS線程可以繼續(xù)mounting其他的虛擬線程,繼續(xù)執(zhí)行。
類似的,筆記本電腦有12個物理內(nèi)核,會虛擬出4個,最后是16個,可以并行處理16個進程。同樣類比,java中原來的一個實際線程可以對應n個虛擬線程,這些虛擬線程可以更多的完成任務,大大提高了并發(fā)程度,而且由于n個虛擬線程對應一個實際線程,虛擬線程之間的切換頻率幾何級下降了。
4、Virtual Threads 該怎么使用
先看下說明,為了方便用戶使用,JDK提供了一系列使用虛擬線程的API,并且兼容之前線程的使用方式,無需為使用虛擬線程而重寫應用程序。
使用靜態(tài)構(gòu)造器方法(新API)
Thread.startVirtualThread(() -> {
System.out.println(Thread.currentThread());
});
Thread.ofVirtual().start(() -> {
System.out.println(Thread.currentThread());
});
使用Executors創(chuàng)建虛擬線程池
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
上述代碼會創(chuàng)建一個無限的虛擬線程池。
使用時候的注意事項
- Thread.setPriority(int) 和 Thread.setDaemon(boolean) 這倆方法對虛擬線程不起作用
- Thread.getThreadGroup() 會返回一個虛擬的空VirtualThreads group。
- 同一個任務使用Virtual Threads和Platform Threads執(zhí)行效率上是完全一樣的,并不會有什么性能上的提升
- 盡量使用JUC包下的并發(fā)控制例如ReentrantLock來進行同步控制,而不使用synchronized 。 synchronized 同步代碼塊會pinning(別住或者說Block)虛擬線程,這點JDK官方說后面有可能會優(yōu)化這點
- Virtual Threads 被設計成final類,并不能使用子類來繼承
- 不適用于計算密集型任務: 虛擬線程適用于I/O密集型任務,但不適用于計算密集型任務,因為它們在同一線程中運行,可能會阻塞其他虛擬線程。
- 新特性自然有很多BUG,這點在JDK的Issue中確實也體現(xiàn)了,使用請慎重?。?img src="https://imgs.yssmx.com/Uploads/2024/02/836176-11.png" alt="jdk21新特性,java,spring,jvm,開發(fā)語言,oracle,redis" referrerpolicy="no-referrer" />
知乎有一個關于 Java 19 虛擬線程的討論,感興趣的可以去看看:
https://www.zhihu.com/question/536743167 。
Java 虛擬線程的詳細解讀和原理可以看下面文章:
JVM 中的線程模型是用戶級的么?
虛擬線程極簡入門
虛擬線程原理及性能分析|得物技術
Java19 正式 GA!看虛擬線程如何大幅提高系統(tǒng)吞吐量
虛擬線程 - VirtualThread 源碼透視
二、Sequedced Collections(有序集合)
新的集合關系圖:
JDK 21引入了一種新的集合類型,即序列化集合。序列化集合通過提供可預測的迭代順序,解決了在多線程環(huán)境下遍歷集合時可能出現(xiàn)的競爭條件和不確定性問題。
List<String> list = new SequencedArrayList<();
list.add("Apple");
list.add("Banana");
list.add("Orange");
for (String fruit : list.reversed()) {// 反向循環(huán)
System.out.println(fruit);// Orange,Banana,Apple
}
在上面的代碼中,我們創(chuàng)建了一個 SequencedArrayList,并向其中添加了一些水果。使用增強的 for 反向循環(huán)遍歷集合時,我們可以確保按照添加的順序輸出水果的名稱:Orange,Banana,Apple。
這種可預測的順序確保了集合在多線程環(huán)境下的一致性和可靠性。
JDK 21 引入了一種新的集合類型:Sequenced Collections(序列化集合,也叫有序集合),這是一種具有確定出現(xiàn)順序(encounter order)的集合(無論我們遍歷這樣的集合多少次,元素的出現(xiàn)順序始終是固定的)。序列化集合提供了處理集合的第一個和最后一個元素以及反向視圖(與原始集合相反的順序)的簡單方法。
Sequenced Collections 包括以下三個接口:
- SequencedCollection
- SequencedSet
- SequencedMap
SequencedCollection 接口繼承了 Collection接口, 提供了在集合兩端訪問、添加或刪除元素以及獲取集合的反向視圖的方法。
interface SequencedCollection<E> extends Collection<E> {
// New Method
SequencedCollection<E> reversed();
// Promoted methods from Deque<E>
void addFirst(E);
void addLast(E);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
}
List 和 Deque 接口實現(xiàn)了SequencedCollection 接口。
這里以 ArrayList 為例,演示一下實際使用效果:
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1); // List contains: [1]
arrayList.addFirst(0); // List contains: [0, 1]
arrayList.addLast(2); // List contains: [0, 1, 2]
Integer firstElement = arrayList.getFirst(); // 0
Integer lastElement = arrayList.getLast(); // 2
List<Integer> reversed = arrayList.reversed();
System.out.println(reversed); // Prints [2, 1, 0]
SequencedSet接口直接繼承了 SequencedCollection 接口并重寫了 reversed() 方法。
interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {
SequencedSet<E> reversed();
}
SortedSet 和 LinkedHashSet 實現(xiàn)了SequencedSet接口。
這里以 LinkedHashSet 為例,演示一下實際使用效果:
LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>(List.of(1, 2, 3));
Integer firstElement = linkedHashSet.getFirst(); // 1
Integer lastElement = linkedHashSet.getLast(); // 3
linkedHashSet.addFirst(0); //List contains: [0, 1, 2, 3]
linkedHashSet.addLast(4); //List contains: [0, 1, 2, 3, 4]
System.out.println(linkedHashSet.reversed()); //Prints [5, 3, 2, 1, 0]
SequencedMap 接口繼承了 Map接口, 提供了在集合兩端訪問、添加或刪除鍵值對、獲取包含 key 的 SequencedSet、包含 value 的 SequencedCollection、包含 entry(鍵值對) 的 SequencedSet以及獲取集合的反向視圖的方法。
interface SequencedMap<K,V> extends Map<K,V> {
// New Methods
SequencedMap<K,V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K,V>> sequencedEntrySet();
V putFirst(K, V);
V putLast(K, V);
// Promoted Methods from NavigableMap<K, V>
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
}
SortedMap 和LinkedHashMap 實現(xiàn)了SequencedMap 接口。
這里以 LinkedHashMap 為例,演示一下實際使用效果:
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.firstEntry(); //1=One
map.lastEntry(); //3=Three
System.out.println(map); //{1=One, 2=Two, 3=Three}
Map.Entry<Integer, String> first = map.pollFirstEntry(); //1=One
Map.Entry<Integer, String> last = map.pollLastEntry(); //3=Three
System.out.println(map); //{2=Two}
map.putFirst(1, "One"); //{1=One, 2=Two}
map.putLast(3, "Three"); //{1=One, 2=Two, 3=Three}
System.out.println(map); //{1=One, 2=Two, 3=Three}
System.out.println(map.reversed()); //{3=Three, 2=Two, 1=One}
三、Deprecate the Windows 32-bit x86 Port for Removal
JEP 449 Deprecate the Windows 32-bit x86 Port for Removal旨在廢棄并最終移除 Windows 32 位 x86 平臺上的 Java 支持。這是基于該平臺已經(jīng)逐漸被淘汰、性能限制和安全問題等原因做出的合理舉措。廢棄該平臺上的 Java 支持可以提高應用程序的性能和安全性,并與現(xiàn)代計算機趨勢相符。開發(fā)者需要及時關注 JEP 449 的實施情況,并根據(jù)需要進行相應的遷移和調(diào)整。Windows 32 位 x86 平臺上的 Java 用戶需要考慮升級到 64 位架構(gòu)的計算機和操作系統(tǒng),以繼續(xù)獲得最新的 Java 更新和功能改進。
四、 Prepare to Disallow the Dynamic Loading of Agents
動態(tài)加載代理禁用準備是一個 Java 增強提案,旨在禁止動態(tài)加載代理以提高應用程序的安全性。它通過修改類加載器、Instrumentation API 和安全管理器來實現(xiàn)禁止動態(tài)加載代理的功能。盡管這樣做可以增加應用程序的安全性,但也可能影響依賴于動態(tài)加載代理的現(xiàn)有代碼。因此,在使用該功能之前需要仔細評估現(xiàn)有代碼的依賴關系。
五、Generational ZGC(分代ZGC)
Generational ZGC 是一種用于 Java 虛擬機的垃圾回收器,旨在提供低延遲和高吞吐量的垃圾回收解決方案。它通過并發(fā)處理和分代回收的策略,實現(xiàn)了非常低的停頓時間,并且能夠處理非常大的堆內(nèi)存。然而,使用 Generational ZGC 需要注意性能開銷和配置復雜性。
六、Pattern Matching for switch(switch 的模式匹配)
Pattern Matching for switch 是 Java 14 中引入的一個新特性,它允許在 switch 語句中使用模式匹配。通過這個特性,我們可以更方便地對變量進行類型判斷和提取。它簡化了對變量類型的判斷和提取邏輯,使代碼更加簡潔、清晰,并且增強了代碼的可讀性和可維護性。但需要注意的是,目前只支持基本數(shù)據(jù)類型和引用類型的模式匹配,不支持其他特殊類型的模式匹配。
七、Record Patterns(記錄模式)
Record Patterns 是 Java 16 引入的一個新特性,它提供了一種簡潔、清晰的方式來進行模式匹配,并且可以方便地從記錄類型中提取字段值。使用 Record Patterns 可以使代碼更加簡潔、可讀,并提高開發(fā)效率。然而,由于記錄類型是不可變的,因此在修改字段值時需要創(chuàng)建新的對象。同時,Record Patterns 目前只能用于記錄類型,不能用于其他類。
八、Key Encapsulation Mechanism API
Key Encapsulation Mechanism API 是一個用于支持密鑰封裝機制的 Java API。它提供了一組方法和類,用于生成、封裝和解封裝密鑰。通過使用公鑰進行密鑰交換,避免了傳統(tǒng)密鑰交換方式中存在的安全風險。API 的實現(xiàn)原理基于非對稱加密算法和密鑰封裝機制,能夠提供較高的安全性和靈活性。開發(fā)者可以輕松地使用 API 進行密鑰封裝和解封裝操作,并與現(xiàn)有的密碼學算法和協(xié)議集成,滿足不同場景的需求。然而,API 的使用需要注意私鑰的安全性和密文的傳輸安全。
九、String Templates(字符串模板)(預覽)
String Templates(字符串模板) 目前仍然是 JDK 21 中的一個預覽功能。String Templates 提供了一種更簡潔、更直觀的方式來動態(tài)構(gòu)建字符串。通過使用占位符${},我們可以將變量的值直接嵌入到字符串中,而不需要手動處理。在運行時,Java 編譯器會將這些占位符替換為實際的變量值。并且,表達式支持局部變量、靜態(tài)/非靜態(tài)字段甚至方法、計算結(jié)果等特性。實際上,String Templates(字符串模板)再大多數(shù)編程語言中都存在:
"Greetings {{ name }}!"; //Angular
`Greetings ${ name }!`; //Typescript
$"Greetings { name }!" //Visual basic
f"Greetings { name }!" //Python
Java 在沒有 String Templates 之前,我們通常使用字符串拼接或格式化方法來構(gòu)建字符串:
//concatenation
message = "Greetings " + name + "!";
//String.format()
message = String.format("Greetings %s!", name); //concatenation
//MessageFormat
message = new MessageFormat("Greetings {0}!").format(name);
//StringBuilder
message = new StringBuilder().append("Greetings ").append(name).append("!").toString();
這些方法或多或少都存在一些缺點,比如難以閱讀、冗長、復雜。
Java 使用 String Templates 進行字符串拼接,可以直接在字符串中嵌入表達式,而無需進行額外的處理:
String message = STR."Greetings \{name}!";
在上面的模板表達式中:
- STR 是模板處理器。
- {name}為表達式,運行時,這些表達式將被相應的變量值替換。
Java 目前支持三種模板處理器:
- STR:自動執(zhí)行字符串插值,即將模板中的每個嵌入式表達式替換為其值(轉(zhuǎn)換為字符串)。
- FMT:和 STR 類似,但是它還可以接受格式說明符,這些格式說明符出現(xiàn)在嵌入式表達式的左邊,用來控制輸出的樣式
- RAW:不會像 STR 和 FMT 模板處理器那樣自動處理字符串模板,而是返回一個 StringTemplate 對象,這個對象包含了模板中的文本和表達式的信息
String name = "Lokesh";
//STR
String message = STR."Greetings \{name}.";
//FMT
String message = STR."Greetings %-12s\{name}.";
//RAW
StringTemplate st = RAW."Greetings \{name}.";
String message = STR.process(st);
除了 JDK 自帶的三種模板處理器外,你還可以實現(xiàn) StringTemplate.Processor 接口來創(chuàng)建自己的模板處理器。
我們可以使用局部變量、靜態(tài)/非靜態(tài)字段甚至方法作為嵌入表達式:
//variable
message = STR."Greetings \{name}!";
//method
message = STR."Greetings \{getName()}!";
//field
message = STR."Greetings \{this.name}!";
還可以在表達式中執(zhí)行計算并打印結(jié)果:
int x = 10, y = 20;
String s = STR."\{x} + \{y} = \{x + y}"; //"10 + 20 = 30"
為了提高可讀性,我們可以將嵌入的表達式分成多行:
String time = STR."The current time is \{
//sample comment - current time in HH:mm:ss
DateTimeFormatter
.ofPattern("HH:mm:ss")
.format(LocalTime.now())
}.";
十、外部函數(shù)和內(nèi)存 API(第三次預覽)
Java 程序可以通過該 API 與 Java 運行時之外的代碼和數(shù)據(jù)進行互操作。通過高效地調(diào)用外部函數(shù)(即 JVM 之外的代碼)和安全地訪問外部內(nèi)存(即不受 JVM 管理的內(nèi)存),該 API 使 Java 程序能夠調(diào)用本機庫并處理本機數(shù)據(jù),而不會像 JNI 那樣危險和脆弱。外部函數(shù)和內(nèi)存 API 在 Java 17 中進行了第一輪孵化,由 JEP 412 提出。第二輪孵化由JEP 419 提出并集成到了 Java 18 中,預覽由 JEP 424 提出并集成到了 Java 19 中。
JDK 21 中是第三次預覽,由 JEP 442 提出。
在 Java 19 新特性有詳細介紹。
十一、未命名模式和變量(預覽)
未命名模式和變量使得我們可以使用下劃線 _ 表示未命名的變量以及模式匹配時不使用的組件,旨在提高代碼的可讀性和可維護性。
未命名變量的典型場景是 try-with-resources 語句、 catch 子句中的異常變量和for循環(huán)。當變量不需要使用的時候就可以使用下劃線 _代替,這樣清晰標識未被使用的變量。
try (var _ = ScopedContext.acquire()) {
// No use of acquired resource
}
try { ... }
catch (Exception _) { ... }
catch (Throwable _) { ... }
for (int i = 0, _ = runOnce(); i < arr.length; i++) {
...
}
未命名模式是一個無條件的模式,并不綁定任何值。未命名模式變量出現(xiàn)在類型模式中。
if (r instanceof ColoredPoint(_, Color c)) { ... c ... }
switch (b) {
case Box(RedBall _), Box(BlueBall _) -> processBox(b);
case Box(GreenBall _) -> stopProcessing();
case Box(_) -> pickAnotherBox();
}
十二、未命名類和實例 main 方法 (預覽)
這個特性主要簡化了 main 方法的的聲明。對于 Java 初學者來說,這個 main 方法的聲明引入了太多的 Java 語法概念,不利于初學者快速上手。
沒有使用該特性之前定義一個 main 方法:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
使用該新特性之后定義一個 main 方法:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
進一步精簡(未命名的類允許我們不定義類名):文章來源:http://www.zghlxwxcb.cn/news/detail-836176.html
void main() {
System.out.println("Hello, World!");
}
備注:
大神參考資料:https://blog.csdn.net/njpkhuan/article/details/133177862
Java 21 String Templates:https://howtodoinjava.com/java/java-string-templates/
Java 21 Sequenced Collections:https://howtodoinjava.com/java/sequenced-collections/文章來源地址http://www.zghlxwxcb.cn/news/detail-836176.html
到了這里,關于JDK21新特性的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!