來源:medium.com
JDK 21 于 2023 年 9 月 19 日發(fā)布,是繼之前的 LTS 版本 JDK 17 之后最新的長期支持 (LTS) 版本。在本文中,我們將探討 JDK 21 新引入的功能。
以下是 JDK 21 的新功能列表:
虛擬線程
序列集合
記錄模式
字符串模板(預(yù)覽)
未命名模式和變量(預(yù)覽)
未命名類和實例主要方法(預(yù)覽)
作用域值(預(yù)覽)
結(jié)構(gòu)化并發(fā)(預(yù)覽)
1 虛擬線程
從 Java 代碼的角度來看,虛擬線程感覺就像普通線程,但它們沒有 1:1 映射到操作系統(tǒng)/平臺線程。它是從虛擬線程到載體線程進而到操作系統(tǒng)線程的M:N映射。
有一個所謂的載體線程池,虛擬線程臨時映射(“安裝”)到該線程池上。一旦虛擬線程遇到阻塞操作,虛擬線程就會從載體線程中移除(“卸載”),并且載體線程可以執(zhí)行另一個虛擬線程(新的或之前被阻塞的虛擬線程)。
載體線程池是ForkJoinPool
虛擬線程的一些優(yōu)點:
提高應(yīng)用程序吞吐量
提高應(yīng)用程序可用性
減少內(nèi)存消耗
創(chuàng)建虛擬線程
要創(chuàng)建虛擬線程,我們可以使用 Thread.ofVirtual() 工廠方法并傳遞可運行對象。
Thread.ofVirtual().start(Runnable);
Thread.ofVirtual().unstarted(Runnable);
如果你想讓虛擬線程立即啟動,你可以使用start() 方法,它會立即執(zhí)行傳遞給它的Runnable start()。
如果不希望虛擬線程立即啟動,可以使用該unstarted()方法。
創(chuàng)建使用虛擬線程的ExecutorService
我們只需要替換newFixedThreadPool為newVirtualThreadPerTaskExecutor
importjava.util.concurrent.ExecutorService; importjava.util.concurrent.Executors; publicclassVirtualThreadExample{ publicstaticvoidmain(String[]args){ ExecutorServiceexecutor=Executors.newVirtualThreadPerTaskExecutor(); executor.submit(()->{ System.out.println(Thread.currentThread().getName()) }); executor.shutdown(); } }
基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
項目地址:https://github.com/YunaiV/ruoyi-vue-pro
視頻教程:https://doc.iocoder.cn/video/
2 順序集合
順序集合為我們提供了defined encounter order(是一種所見即所得的順序,含義是從隊列中取出元素的順序既是你存放該元素時候的順序),用于訪問第一個和最后一個元素并以相反的順序迭代。
這意味著我們可以在集合的兩端添加、檢索或刪除元素。
publicinterfaceSequencedCollectionextendsCollection { defaultvoidaddFirst(Ee){...} defaultvoidaddLast(Ee){...} defaultEgetFirst(){...} defaultEgetLast(){...} defaultEremoveFirst(){...} defaultEremoveLast(){...} SequencedCollection reversed(); }
正如我們所看到的,除了reverse()之外的所有方法都是默認方法并提供默認實現(xiàn)。
這意味著現(xiàn)有的集合類(例如 ArrayList 和 LinkedList)都可以實現(xiàn)此接口,而無需更改其代碼。
ArrayListlist=newArrayList<>(); list.add(1);//[1] list.addFirst(0);//[0,1] list.addLast(2);//[0,1,2] list.getFirst();//0 list.getLast();//2 list.reversed();//[2,1,0]
SequencedSet
SequencedSet 接口對于具有有序元素的 Set 非常有用,特別是當(dāng)您必須執(zhí)行某些操作(例如檢索或刪除第一個或最后一個索引處的元素)時。它還提供了一種反轉(zhuǎn)元素的方法。
您還需要知道兩個 SequencedSet 對象的比較與其他類型的 Set 相同,不依賴于元素順序。
interfaceSequencedSetextendsSet ,SequencedCollection { SequencedSet reversed(); }
LinkedHashSet 是 Set 的一個實現(xiàn),它實現(xiàn)了 SequencedSet 接口。
因此,您可以使用 LinkedHashSet 來創(chuàng)建 SequencedSet。
Set 的其他實現(xiàn)(例如 HashSet 和 TreeSet)未實現(xiàn)該接口。
讓我們探索一些示例來演示如何訪問第一個和最后一個元素,以及如何使用反向函數(shù):
SequencedSetvalues=newLinkedHashSet<>(); values.add("one"); values.add("two"); System.out.println(values);//[one,two] values.addFirst("zero"); System.out.println(values);//[zero,one,two] values.addFirst("one"); System.out.println(values);//[one,zero,two] values.addLast("three"); System.out.println(values);//[one,zero,two,three] values.removeFirst(); System.out.println(values);//[zero,two,three] SequencedSet reversedSet=values.reversed(); System.out.println(reversedSet);//[three,two,zero] booleanisEqual=values.equals(reversedSet); System.out.println(isEqual);//true System.out.println(values.hashCode());//612888 System.out.println(reversedSet.hashCode());//612888 System.out.println(values.hashCode()==reversedSet.hashCode());//true
SequencedMap
如果要使用 SequencedMap 中定義的新方法,則需要使用 Map 實現(xiàn),例如 LinkedHashMap 或?qū)崿F(xiàn) SortedMap 的 Map。
HashMap 不利用 Sequenced Collections,因為它沒有定義 defined encounter order(是一種所見即所得的順序,含義是從隊列中取出元素的順序既是你存放該元素時候的順序)。
interfaceSequencedMapextendsMap { SequencedMap reversed(); SequencedSet sequencedKeySet(); SequencedCollection sequencedValues(); SequencedSet >sequencedEntrySet(); VputFirst(K,V); VputLast(K,V); Entry firstEntry(); Entry lastEntry(); Entry pollFirstEntry(); Entry pollLastEntry(); }
在下面的示例中,正如您所看到的,我們可以通過firstEntry()和lastEntry()方法訪問第一個和最后一個元素。
pollFirstEntry()方法將刪除并返回第一個鍵值元素,如果映射為空,則返回 null。
此外,調(diào)用reverse()只會比較元素,而不依賴于它們的順序。
SequencedMapmyMap=newLinkedHashMap<>(); myMap.put("one",1); myMap.put("two",2); System.out.println(myMap);//{one=1,two=2} Entry firstEntry=myMap.firstEntry(); System.out.println(firstEntry);//one=1 Entry lastEntry=myMap.lastEntry(); System.out.println(lastEntry);//two=2 myMap.putFirst("zero",0); System.out.println(myMap);//{zero=0,one=1,two=2} myMap.putFirst("one",-1); System.out.println(myMap);//{one=-1,zero=0,two=2} Entry polledFirstEntry=myMap.pollFirstEntry(); System.out.println(polledFirstEntry);//one=-1 System.out.println(myMap);//{zero=0,two=2} SequencedMap reversedMap=myMap.reversed(); System.out.println(reversedMap);//{two=2,zero=0} booleanisEqual=myMap.equals(reversedMap); System.out.println(isEqual);//true System.out.println(myMap.hashCode());//692224 System.out.println(reversedMap.hashCode());//692224 System.out.println(myMap.hashCode()==reversedMap.hashCode());//true
3 字符串模板
這是預(yù)覽功能,默認禁用,我們需要使用
--enable-preview啟用字符串模板。
首先,在深入探討字符串模板之前,我將探討一些用于組合字符串的技術(shù)。
+(加號)運算符: 最大的缺點是每次使用 + 運算符時都會創(chuàng)建一個新字符串。
StringBuffer 和 StringBuilder: StringBuffer 是線程安全的,而 StringBuilder 是在 Java 5 中添加的,性能更高,但不是線程安全的替代方案。
它們的主要缺點是冗長,尤其是對于更簡單的字符串:
vargreeting=newStringBuilder() .append("Hello,welcome") .append(name) .toString();
String::format 和 String::formatter: 它們允許可重用模板,但它們要求我們指定格式并以正確的順序提供變量。
varformat="Goodmorning%s,It'sabeautifulday!"; vartext=String.format(format,name); //Java15+ vartext=format.formatter(name);
盡管我們節(jié)省了字符串分配的數(shù)量,但現(xiàn)在 JVM 必須解析/驗證模板字符串。
java.text.MessageFormat: 與String格式相同,但更詳細
varformat=newMessageFormat("Goodmorning{0},It'sabeautifulday!"); vargreeting=format.format(name);
現(xiàn)在我們有字符串模板來拯救
它簡單、簡潔,處理字符串的新方法稱為模板表達式。它們可以執(zhí)行插值,還為我們提供了組合字符串的靈活性,并將結(jié)構(gòu)化文本轉(zhuǎn)換為任何對象,而不僅僅是字符串。
模板表達式由三個組成部分組成:
模板處理器:Java 提供了兩種用于執(zhí)行字符串插值的模板處理器:STR 和 FMT
包含包裝表達式的模板,如 {name}
點 (.) 字符
以下是一些關(guān)于如何將字符串模板與模板處理器一起使用的示例:
packagecom.mina.stringtemplates; importstaticjava.util.FormatProcessor.FMT; importjava.time.LocalDate; importjava.time.format.DateTimeFormatter; publicclassStringTemplateExamples{ publicstaticStringgreeting(StringfirstName,StringlastName){ returnSTR."Hello!Goodmorning{firstName}{lastName}"; } publicstaticStringmultiplyWithArithmeticExpressions(inta,intb){ returnSTR."{a}times{b}={a*b}"; } publicstaticStringmultiplyWithJavaExpression(inta,intb){ returnSTR."{a}times{b}={Math.multiplyExact(a,b)}"; } //multiplicationwithfloatingpointnumbersroundedtotwodecimalplacesusingtheFMTtemplateprocessor publicstaticStringmultiplyFloatingNumbers(doublea,doubleb){ returnFMT."%.2f{a}times%.2f{b}=%.2f{a*b}"; } publicstaticStringgetErrorResponse(inthttpStatus,StringerrorMessage){ returnSTR.""" { "httpStatus":{httpStatus}, "errorMessage":"{errorMessage}" }"""; } publicstaticStringgetCurrentDate(){ returnSTR."Today'sdate:{ LocalDate.now().format( DateTimeFormatter.ofPattern("yyyy-MM-dd") )}"; } }
4 記錄模式
記錄模式匹配是一種在單個步驟中匹配記錄類型并訪問其組件的方法。
我們用它來測試一個值是否是記錄類類型的實例,如果是,則對其組件值執(zhí)行模式匹配。
下面的示例測試是否是具有記錄模式transaction的記錄實例TransactionTransaction(String type, double amount)
packagecom.mina.recordpattern; publicclassRecordPatternExample{ //I'musing"_"forreadabilityhere,thiswon'tcompile publicstaticStringgetTransactionType(Transactiontransaction){ returnswitch(transaction){ casenull->thrownewIllegalArgumentException("Transactioncannotbenull."); caseTransaction(Stringtype,doubleamount)whentype.equals("Deposit")&&amount>0->"Deposit"; caseTransaction(Stringtype,_)whentype.equals("Withdrawal")->"Withdrawal"; default->"Unknowntransactiontype"; }; } recordTransaction(Stringtype,doubleamount){ } }
如果事務(wù)為空,會發(fā)生什么?你是對的——拋出了一個空指針異常。這也是Java 21中的情況,但是現(xiàn)在我們可以通過寫case null->來顯式地使用null case,這樣可以避免NullPointerException。
保護模式:也可以保護特定情況。例如,我們使用when關(guān)鍵字來檢查相等性。
5 switch 模式匹配
switch模式匹配在 Java 17 中作為預(yù)覽功能引入,并在 Java 21 中永久保留。
語句switch將控制轉(zhuǎn)移到多個語句或表達式之一,具體取決于其選擇器表達式的值(可以是任何類型),并且case標簽可以具有模式。
它檢查其選擇器表達式是否與模式匹配,與測試其選擇器表達式是否完全等于常量相比,這更具可讀性和靈活性。
packagecom.mina.switchpatternmatching; importcom.mina.switchpatternmatching.SwitchPatternMatchingExample.Transaction.Deposit; importcom.mina.switchpatternmatching.SwitchPatternMatchingExample.Transaction.Withdrawal; publicclassSwitchPatternMatchingExample{ publicstaticStringgetTransactionType(Transactiontransaction){ returnswitch(transaction){ casenull: thrownewIllegalArgumentException("Transactioncan'tbenull."); caseDepositdepositwhendeposit.getAmount()>0://Guardedpatternwithwhenclause yield"Deposit"; caseWithdrawalwithdrawal: yield"Withdrawal"; default: yield"Unknowntransactiontype"; }; } sealedclassTransactionpermitsDeposit,Withdrawal{ privatedoubleamount; publicTransaction(doubleamount){ this.amount=amount; } publicdoublegetAmount(){ returnamount; } finalclassWithdrawalextendsTransaction{ publicWithdrawal(doubleamount){ super(amount); } } finalclassDepositextendsTransaction{ publicDeposit(doubleamount){ super(amount); } } } }
希望這篇文章可以幫助您更好地了解 Java 21 的新功能!
-
JAVA
+關(guān)注
關(guān)注
20文章
2984瀏覽量
106831 -
字符串
+關(guān)注
關(guān)注
1文章
589瀏覽量
21095 -
代碼
+關(guān)注
關(guān)注
30文章
4886瀏覽量
70239 -
線程
+關(guān)注
關(guān)注
0文章
507瀏覽量
20070
原文標題:Java 21 新特性的實踐,確實很絲滑!
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
一文了解Java中的構(gòu)造器
PowerDesigner 10.0的新功能
Altium Designer 20 新功能官方直播
21天學(xué)通JAVA2_袁國忠譯

Java學(xué)習(xí)路線教程之Java新手必須學(xué)習(xí)那21個技術(shù)點詳細資料說明
微軟開始測試 21H1 的功能體驗包功能 無需大版本更新即可體驗
蘋果ios15新功能
文心一言APP上線數(shù)字分身功能
淺談Xpedition 2409版本的新功能
Java 23功能介紹

評論