JDK17 與 JDK11 特性差異淺談
1.1 switch 表達(dá)式語(yǔ)法變化
- 在 JDK12 之前如果 switch 忘記寫(xiě) break 將導(dǎo)致貫穿,在 JDK12 中對(duì) switch 的這一貫穿性做了改進(jìn)。你只要將 case 后面的冒號(hào)改成箭頭,那么你即使不寫(xiě)break也不會(huì)貫穿了。
- switch 可作為表達(dá)式,不再是單獨(dú)的語(yǔ)句。
- 當(dāng)你把 switch 中的 case 后的冒號(hào)改為箭頭之后,此時(shí) switch 就不會(huì)貫穿了,但在某些情況下,程序本來(lái)就希望貫穿比如我就希望兩個(gè) case 共用一個(gè)執(zhí)行體。JDK12 的 switch 中的 case 也支持多值匹配,這樣程序就變得更加簡(jiǎn)潔了。
- JDK13 引入了一個(gè)新的 yield 語(yǔ)句來(lái)產(chǎn)生一個(gè)值,該值成為封閉的 switch 表達(dá)式的值。yield 和 return 的主要區(qū)別在于它們?nèi)绾慰刂瞥绦虻牧鞒?。return 會(huì)結(jié)束當(dāng)前的方法或函數(shù),并將控制權(quán)返回給調(diào)用者。而 yield 則會(huì)暫時(shí)離開(kāi)當(dāng)前的 switch 表達(dá)式,將一個(gè)值返回給調(diào)用者,然后再回到 switch 表達(dá)式的地方繼續(xù)執(zhí)行。
public class Demo{
public static void main(String[] args){
var score = 'C';
// 執(zhí)行switch分支語(yǔ)句
String s = switch (score){
case 'A', 'B' -> "上等";
case 'C' -> "中等";
case 'D', 'E' -> "下等";
default -> {
if (score > 100) {
yield "數(shù)據(jù)不能超過(guò)100";
} else {
yield score + "此分?jǐn)?shù)低于0分";
}
}
}
}
}
1.2 微基準(zhǔn)測(cè)試套件
JMH ,即 Java Microbenchmark Harness ,是專門(mén)用于代碼微基準(zhǔn)測(cè)試的工具套件。
JMH 典型的應(yīng)用場(chǎng)景
- 想定量地知道某個(gè)方法需要執(zhí)行多長(zhǎng)時(shí)間,以及執(zhí)行時(shí)間和輸入?yún)?shù)的相關(guān)性。
- 一個(gè)接口有兩種不同實(shí)現(xiàn),希望比較哪種實(shí)現(xiàn)性能更好。
JMH 使用案例
增加 JMH 的依賴
<properties>
<jmh.version>1.14.1</jmh.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
代碼編寫(xiě)
import org.openjdk.jmh.annotations.*;
@State(Scope.Thread)
public class MyBenchmark {
@Benchmark
@BenchmarkMode(Mode.All)
public void testMethod() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Benchmark
@BenchmarkMode(Mode.All)
public void testMethod2() {
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
public class BenchmarkRunner {
public static void main(String[] args) throws Exception {
Options opt = new OptionsBuilder()
.include(MyBenchmark.class.getSimpleName())
.forks(1)
.warmupIterations(5)
.measurementIterations(5)
.build();
new Runner(opt).run();
}
}
//以下這些方法都是JMH的一部分,可以在任何版本的JMH中使用。
//include(SimpleBenchmark.class.getSimpleName()) :這個(gè)方法表示你想要運(yùn)行哪個(gè)類的基準(zhǔn)測(cè)試。
//exclude("xxx") :這個(gè)方法表示你想要在基準(zhǔn)測(cè)試中排除哪個(gè)方法。
//forks(1) :這個(gè)方法表示你想要進(jìn)行多少輪的基準(zhǔn)測(cè)試。每一輪測(cè)試都會(huì)在一個(gè)新的 JVM 進(jìn)程中進(jìn)行,以確保每輪測(cè)試的環(huán)境是獨(dú)立的。
//warmupIterations(5) :這個(gè)方法表示你想要進(jìn)行多少次預(yù)熱迭代。預(yù)熱迭代是為了讓 JVM 達(dá)到穩(wěn)定狀態(tài),預(yù)熱迭代的結(jié)果不會(huì)被計(jì)入最終的基準(zhǔn)測(cè)試結(jié)果。
//measurementIterations(5) :這個(gè)方法表示你想要進(jìn)行多少次正式的基準(zhǔn)測(cè)試迭代,這些迭代的結(jié)果會(huì)被用來(lái)計(jì)算基準(zhǔn)測(cè)試的最終結(jié)果。
結(jié)果輸出(只截取了一部分)
圖片
圖片
相關(guān)注解
@BenchmarkMode
對(duì)應(yīng) Mode 選項(xiàng),可用于類或者方法上,需要注意的是,這個(gè)注解的 value 是一個(gè)數(shù)組,可以把幾種 Mode 集合在一起執(zhí)行,還可以設(shè)置為 Mode.All ,即全部執(zhí)行一遍。
圖片
- 吞吐量 (thrpt) :?jiǎn)挝粫r(shí)間內(nèi)完成的操作次數(shù),也可以理解為每秒能處理的事務(wù)數(shù)。
- 平均時(shí)間 (avgt) :每次操作所需的平均時(shí)間。
- 樣本時(shí)間 (sample) :隨機(jī)取樣,最后輸出取樣結(jié)果的分布,例如“99%的調(diào)用在xxx毫秒以內(nèi),99.99%的調(diào)用在xxx毫秒以內(nèi)”。
- 單次啟動(dòng)時(shí)間 (ss) :SingleShotTime ,度量一次操作的運(yùn)行時(shí)間,即每次迭代前都會(huì)重新初始化狀態(tài)。
@State
類注解,JMH 測(cè)試類必須使用 @State 注解,State 定義了一個(gè)類實(shí)例的生命周期,可以類比 SpringBean 的 Scope 。由于 JMH 允許多線程同時(shí)執(zhí)行測(cè)試,不同的選項(xiàng)含義如下:
Scope.Thread :默認(rèn)的 State ,每個(gè)測(cè)試線程分配一個(gè)實(shí)例。
Scope.Benchmark :所有測(cè)試線程共享一個(gè)實(shí)例,用于測(cè)試有狀態(tài)實(shí)例在多線程共享下的性能。
Scope.Group :每個(gè)線程組共享一個(gè)實(shí)例。
如果你想測(cè)試一個(gè)對(duì)象在多線程環(huán)境下的行為,你可以選擇 Scope.Benchmark 。如果你想要每個(gè)線程都有自己的狀態(tài),你可以選擇 Scope.Thread 。如果你想要在同一線程組內(nèi)的所有線程共享狀態(tài),你可以選擇 Scope.Group 。
@OutputTimeUnit
benchmark 結(jié)果所使用的時(shí)間單位,可用于類或者方法注解,使用 java.util.concurrent.TimeUnit 中的標(biāo)準(zhǔn)時(shí)間單位。
@Benchmark
方法注解,表示該方法是需要進(jìn)行 benchmark 的對(duì)象。
1.3 生成類數(shù)據(jù)共享特性優(yōu)化
背景:在同一個(gè)物理機(jī)上啟動(dòng)多個(gè) JVM 時(shí),如果每個(gè)虛擬機(jī)都單獨(dú)裝載自己需要的所有類,啟動(dòng)成本和內(nèi)存占用是比較高的。所以引入了類數(shù)據(jù)共享機(jī)制 ( Class Data Sharing ,簡(jiǎn)稱 CDS ) 的概念,通過(guò)把一些核心類在每個(gè) JVM 間共享,每個(gè) JVM 只需要裝載自己的應(yīng)用類即可。
JDK12 之前想要利用 CDS 的用戶,必須 -Xshare:dump 作為額外的步驟來(lái)運(yùn)行。
JDK12 針對(duì) 64 位平臺(tái)使其默認(rèn)生成類數(shù)據(jù)共享 ( CDS ) 歸檔。
好處:JVM啟動(dòng)時(shí)間減少了。因?yàn)楹诵念愂枪蚕淼?,所?JVM 的內(nèi)存占用也減少了。
JDK13 則支持在應(yīng)用運(yùn)行之后進(jìn)行動(dòng)態(tài)歸檔。需要使用 -XX:ArchiveClassesAtExit=filename.jsa 選項(xiàng)來(lái)指定一個(gè)文件,JVM 會(huì)在退出時(shí)將應(yīng)用程序類和標(biāo)準(zhǔn)庫(kù)類的元數(shù)據(jù)寫(xiě)入這個(gè)文件。然后,在下一次啟動(dòng) JVM 時(shí),你可以使用 -XX:SharedArchiveFile=filename.jsa 選項(xiàng)來(lái)指定剛才創(chuàng)建的文件。
好處:這個(gè)特性允許 JVM 在運(yùn)行時(shí)捕獲類的元數(shù)據(jù),然后在下一次 JVM 啟動(dòng)時(shí)重用這些元數(shù)據(jù),從而提高啟動(dòng)速度和減少內(nèi)存占用。
1.4 G1 垃圾收集器和 ZGC 功能增強(qiáng)
- 在 JDK12 之前,一旦開(kāi)始執(zhí)行垃圾收集,即使可能會(huì)超過(guò) -XX:MaxGCPauseMillis 參數(shù)設(shè)定的值,也不會(huì)停止。JDK12 中,如果 G1 垃圾收集器有可能超過(guò)預(yù)期的暫停時(shí)間,則可以終止。G1 垃圾收集器發(fā)現(xiàn)反復(fù)標(biāo)記過(guò)多的區(qū)域后,G1 就會(huì)切換到更增量的一種 Mix GC 。它將待回收的集合分為兩個(gè)部分:強(qiáng)制回收和可選回收。強(qiáng)制回收的部分是 G1 無(wú)法增量回收的部分(比如年輕代)也可以包含能增量回收的部分(比如老年代)來(lái)提升性能,它占待回收集合的 80% 。當(dāng) G1 回收完強(qiáng)制回收部分后,如果還有多余的時(shí)間,將會(huì)更細(xì)粒度的處理可選回收部分,每次只會(huì)處理一個(gè)區(qū)域,避免超過(guò)用戶指定的時(shí)間。在處理完一個(gè)區(qū)域后,G1 會(huì)根據(jù)剩余的時(shí)間來(lái)決定是否繼續(xù)處理剩余的可選部分。
- JDK12 中,如果應(yīng)用程序活動(dòng)非常低,G1 可以使其能夠在空閑時(shí)自動(dòng)將 Java 堆內(nèi)存返還給操作系統(tǒng)。
- JDK13 中,ZGC 也能夠主動(dòng)釋放未使用內(nèi)存給操作系統(tǒng),但可以通過(guò) -XX : -ZUncommit 參數(shù)來(lái)顯示關(guān)閉此功能。ZGC 支持最大堆大小為 16TB ,可以滿足大多數(shù)大型服務(wù)器的需求。
- ZGC 是在 JDK11 中引入的垃圾回收器,但一直都是實(shí)驗(yàn)版本,在 JDK15 中正式上線,如果你的應(yīng)用程序需要處理非常大的堆或者更低的暫停時(shí)間,那么 ZGC 可能是一個(gè)更好的選擇。如果你對(duì)兼容性和穩(wěn)定性有更高的要求,因?yàn)?G1 經(jīng)過(guò)長(zhǎng)時(shí)間的驗(yàn)證和優(yōu)化,可能 G1 更適合。
1.5 ShenandoahGC
添加一個(gè)名為 Shenandoah 的新垃圾收集算法,通過(guò)與正在運(yùn)行的 Java 線程同時(shí)進(jìn)行疏散工作來(lái)減少 GC 暫停時(shí)間,最終目標(biāo)旨在針對(duì) JVM 上的內(nèi)存收回實(shí)現(xiàn)低停頓的需求。
Shenandoah 是以實(shí)驗(yàn)特性在 JDK12 中引入的。在 JDK15 中正式上線。
使用 Shenandoah 的暫停時(shí)間與堆大小無(wú)關(guān),這意味著無(wú)論堆是 200MB 還是 200GB ,都將具有相同的一致暫停時(shí)間。與 ZGC 類似,Shenandoah GC 主要目標(biāo)是 99.9% 的暫停小于 10ms ,暫停與堆大小無(wú)關(guān)等。
ZGC 和 ShenandoahGC 的一些主要區(qū)別:
- 設(shè)計(jì)目標(biāo):ZGC 和 ShenandoahGC 都是為了實(shí)現(xiàn)低延遲的垃圾收集而設(shè)計(jì)的。
- 并發(fā)回收:ShenandoahGC 實(shí)現(xiàn)了并發(fā)回收,這意味著它可以在應(yīng)用線程運(yùn)行的同時(shí)進(jìn)行垃圾收集,從而減少了垃圾收集對(duì)應(yīng)用性能的影響。
- 內(nèi)存管理:ShenandoahGC 使用名為“連接矩陣”的全局?jǐn)?shù)據(jù)結(jié)構(gòu)來(lái)記錄跨 Region 的引用關(guān)系,降低了處理跨代指針時(shí)的記憶集維護(hù)消耗。而 ZGC 的 Region 可以動(dòng)態(tài)創(chuàng)建和銷毀,容量也可以動(dòng)態(tài)調(diào)整。
- 開(kāi)發(fā)者:Shenandoah 由 RedHat 開(kāi)發(fā),而 ZGC 由 Oracle 開(kāi)發(fā)。
使用方法:要啟用/使用 Shenandoah GC,需要以下 JVM 選項(xiàng): -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC。作為實(shí)驗(yàn)性功能,Shenandoah 構(gòu)建系統(tǒng)會(huì)自動(dòng)禁用不受支持的配置。
1.6 String 新增方法
- transform(Function):對(duì)字符串進(jìn)行處理后返回。
var rs = "test".transform(s -> s + "Java").transform(s -> s.toUpperCase());
// TESTJAVA
- indent:該方法允許我們調(diào)整String實(shí)例的縮進(jìn)。
String result = "Java\njava\ntest".indent(3);
/*結(jié)果會(huì)縮進(jìn)三格
Java
java
test
*/
1.7 Files 新增 mismatch 方法
返回內(nèi)容第一次不匹配的字符位置索引。
System.out.println(Files.mismatch(Path.of("a.txt"),Path.of("b.txt")));
1.8 核心庫(kù) java.text 支持壓縮數(shù)字格式
NumberFormat 添加了對(duì) ”緊湊形式格式化數(shù)字“ 的支持。
”緊湊數(shù)字格式“是指以簡(jiǎn)短或人類可讀形式表示的數(shù)字。
例如,在 en_US 語(yǔ)言環(huán)境中,1000 可以格式化為 “ 1K ”,1000000 可以格式化為 “ 1M ”,具體取決于指定的樣式 NumberFormat.Style 。緊湊數(shù)字格式由 LDML 的 Compact Number 格式規(guī)范定義。要獲取實(shí)例,請(qǐng)使用 NumberFormat 緊湊數(shù)字格式所給出的工廠方法之一。
NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
String result = fmt.format(1000);
//1K
var cnf = NumberFormat.getCompactNumberInstance(Locale.CHINA,NumberFormat.Style.SHORT);
System.out.println(cnf.format(5_0000));
//"5萬(wàn)"
System.out.println(cnf.format(7_9200));
//"7.9萬(wàn)"
System.out.println(cnf.format(8_000_000));
//"800萬(wàn)"
System.out.println(cnf.format(9L << 30));
//"96億"
System.out.println(cnf.format(6L << 50));
//"5637142兆"
System.out.println(cnf.format(6L << 60));
//"6917529京"
1.9 JDK17 支持到 Unicode13
JDK12 支持 Unicode11.0
JDK13 支持 Unicode12.1
從 JDK14 到 JDK17 均是支持 Unicode13.0
1.10 NullPointerExceptions 升級(jí)
JDK14 之前,從報(bào)錯(cuò)中我們只能得到錯(cuò)誤出現(xiàn)的行數(shù),但在 JDK14 之后,會(huì)清晰的告訴你哪個(gè)對(duì)象空指針了。
Exception in thread "main" java.lang.NullPointerException:
Cannot invoke "String.charAt(int)" because "str" is null
at com.qf.jdk14.Test.main(Test.java:11)
1.11 文本塊特性
背景:在 Java 中,在字符串文字中嵌入 HTML ,XML ,SQL 或 JSON 片段通常需要先進(jìn)行轉(zhuǎn)義和串聯(lián)的大量編輯,然后才能編譯包含該片段的代碼。該代碼段通常難以閱讀且難以維護(hù)。
Java 的文本塊特性是在 JDK15 中正式實(shí)現(xiàn)的。這個(gè)特性首先在 JDK13 中以預(yù)覽版的形式發(fā)布,然后在 JDK14 中改進(jìn)并再次以預(yù)覽版的形式發(fā)布。這一特性提高了 Java 程序書(shū)寫(xiě)大段字符串文本的可讀性和方便性。
文本塊的開(kāi)頭定界符是由三個(gè)雙引號(hào) """ 開(kāi)始,從新的一行開(kāi)始字符串的內(nèi)容,以 """ 結(jié)束。如果結(jié)束的 """ 另起一行時(shí),字符串內(nèi)容最后會(huì)留有一新行。
使用案例
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `ID`, `LAST_NAME`;";
//使用文本塊語(yǔ)法
String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;""";
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
//使用文本塊語(yǔ)法
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
縮進(jìn)示例
Java 編譯器會(huì)自動(dòng)刪除不需要的縮進(jìn):
- 每行結(jié)尾的空格都會(huì)刪除。
- 每行開(kāi)始的共有的空格會(huì)自動(dòng)刪除。
- 只保留相對(duì)縮進(jìn)。
- 新行 """ 結(jié)束時(shí),將 """ 向左調(diào)整,則可以給所有行前加相應(yīng)數(shù)量的空格。將 """ 向右調(diào)整,沒(méi)有作用。
System.out.println("""
Hello,
multiline
text blocks!
""");
// 結(jié)果
// > Hello,
// > multiline
// > text blocks!
1.12 重新實(shí)現(xiàn)舊版 Socket API
背景:現(xiàn)在已有的 java.net.Socket 和 java.net.ServerSocket 以及它們的實(shí)現(xiàn)類,都可以回溯到 JDK1.0 時(shí)代了。原始 socket 的維護(hù)和調(diào)試都很痛苦。實(shí)現(xiàn)類還使用了線程棧作為 I/O 的緩沖,導(dǎo)致在某些情況下還需要增加線程棧的大小。該實(shí)現(xiàn)還存在幾個(gè)并發(fā)問(wèn)題,需要徹底解決。在未來(lái)的網(wǎng)絡(luò)世界,要快速響應(yīng),不能阻塞本地方法線程,當(dāng)前的實(shí)現(xiàn)不適合使用了。
JDK13 全新實(shí)現(xiàn)的 NioSocketImpl 來(lái)替換 JDK1 的 SocketImpl 和 PlainSocketImpl。
- 它便于維護(hù)和調(diào)試,與 NewI/O (NIO) 使用相同的 JDK 內(nèi)部結(jié)構(gòu),因此不需要使用系統(tǒng)本地代碼。
- 它與現(xiàn)有的緩沖區(qū)緩存機(jī)制集成在一起,這樣就不需要為 I/O 使用線程棧。
- 它使用 java.util.concurrent 鎖,而不是 synchronized 同步方法,增強(qiáng)了并發(fā)能力。
- 新的實(shí)現(xiàn)是 JDK13 中的默認(rèn)實(shí)現(xiàn),但是舊的實(shí)現(xiàn)還沒(méi)有刪除,可以通過(guò)設(shè)置參數(shù) -Djdk.net.usePlainSocketImpl=true 來(lái)切換到舊版本。
1.13 Hidden Classes
通常我們?cè)谑褂么笮偷目蚣芑蛘?lambda 表達(dá)式的時(shí)候,會(huì)動(dòng)態(tài)生成很多類。但是不幸的是標(biāo)準(zhǔn)的定義類的API:ClassLoader::defineClass 和 Lookup::defineClass 不能夠區(qū)分出這些類是動(dòng)態(tài)生成(運(yùn)行時(shí)生成)的還是靜態(tài)生成(編譯生成)的。
一般來(lái)說(shuō)動(dòng)態(tài)生成的類生命周期更短,并且其可?性要更低。但是現(xiàn)有的 JDK 并沒(méi)有這個(gè)功能。
所有有了 HiddenClasses 的提案,通過(guò) HiddenClasses ,不管是 JDK 還是 JDK 外部的框架,在生成動(dòng)態(tài)類的時(shí)候都可以定義為 HiddenClasses,這樣可以更加有效的控制這些動(dòng)態(tài)生成類的生命周期和可?性。
1.14 instanceof 關(guān)鍵詞
instanceof關(guān)鍵詞主要用來(lái)判斷某個(gè)對(duì)象是不是某個(gè)類的實(shí)例。
比如,有時(shí)候我們要處理一個(gè)類似這樣的數(shù)據(jù)集:
Map<String, Object> data = new HashMap<>();
data.put("test", "111");
data.put("test2", 222);
JDK16 之前需要先判斷獲取的 value 是否是 String ,再做強(qiáng)制類型轉(zhuǎn)換:
Object value = data.get("test");
if (value instanceof String)
{
String s = (String) value;
System.out.println(s.substring(1));
}
在 JDK16 的增強(qiáng)之后,對(duì)于 instanceof 的判斷以及類型轉(zhuǎn)換可以合二為一了:
Object value = data.get("test");
if (value instanceof String s)
{
System.out.println(s.substring(1));
}
1.15 檔案類
Records 的目標(biāo)是擴(kuò)展 Java 語(yǔ)言語(yǔ)法,Records 為聲明類提供了一種緊湊的語(yǔ)法,通過(guò)對(duì)類做這樣的聲明,編譯器可以通過(guò)自動(dòng)創(chuàng)建所有方法并讓所有字段參與 hashCode() 等方法。其目的是為了充當(dāng)不可變數(shù)據(jù)的透明載體的類。
舊方法定義實(shí)體類,代碼如下:
public final class User {
final String name;
final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
通過(guò) Record 類方式,一句話就可以實(shí)現(xiàn)以上功能,代碼如下:
public record User(String username, String password) {}
在 JDK16 之前的版本中,我們不能在類名后面直接寫(xiě)參數(shù)來(lái)定義類的狀態(tài)。這是 JDK16 引入 record 類的一個(gè)新特性。
調(diào)用 Record 類方式,如下:
public class App {
public static void main(String[] args) {
User user = new User("user", "123456");
System.out.println(user.username());
}
}
注意事項(xiàng):
- record 類不允許使用 abstract 關(guān)鍵字定義為抽象
- 所有成員變量均為 final 修飾,不允許再次賦值
- 允許出現(xiàn)靜態(tài)變量/實(shí)例方法/靜態(tài)方法
- 允許出現(xiàn)其他構(gòu)造方法,但必須調(diào)用 record 構(gòu)造方法
- Record 不允許 extends 繼承其他類
1.16 密封類
在 JDK15 中,Java 提出了密封類( Sealed Classes )的概念,在 JDK17 中被正式確認(rèn)。密封類允許類和接口定義其允許的子類型。因此,如果一個(gè)類沒(méi)有顯式地使用 sealed 、non-sealed 或 final 關(guān)鍵字,那么它的默認(rèn)權(quán)限就是 non-sealed 。
以下是一個(gè)密封類的代碼示例:
sealed class Human permits Kyrie, LeBron, George {
public void printName() {
System.out.println("Default");
}
}
non-sealed class Kyrie extends Human {
public void printName() {
System.out.println("Bob");
}
}
sealed class LeBron extends Human {
public void printName() {
System.out.println("Mike");
}
}
final class George extends Human {
public void printName() {
System.out.println("Yannick");
}
}
在這個(gè)例子中,Human 是一個(gè)密封類,它只允許 Kyrie,LeBron 和 George 這三個(gè)類繼承。這樣,我們就可以更精確地控制哪些類可以繼承 Human 類,從而提高代碼的安全性和可讀性。
1.17 統(tǒng)一日志異步刷新
在 JDK17 中,引入了一項(xiàng)新特性:統(tǒng)一日志異步刷新。
先將日志寫(xiě)入緩存,然后再異步地刷新到日志文件,這樣寫(xiě)日志的操作就不會(huì)阻塞執(zhí)行業(yè)務(wù)邏輯的線程,從而提高了程序的運(yùn)行效率。這個(gè)特性對(duì)于需要大量日志輸出,并且對(duì)性能有較高要求的應(yīng)用來(lái)說(shuō),是一個(gè)非常實(shí)用的改進(jìn)??梢酝ㄟ^(guò)傳遞命令行選項(xiàng) -Xlog:async 來(lái)開(kāi)啟此功能。
總結(jié)
從 JDK11 到 JDK17 ,Java 的發(fā)展經(jīng)歷了一系列重要的里程碑。其中最重要的是 JDK17 的發(fā)布,這是一個(gè)長(zhǎng)期支持(LTS)版本,它將獲得長(zhǎng)期的更新和支持,有助于保持程序的穩(wěn)定性和可靠性。此外,Java 的性能也有了顯著的提升。這些進(jìn)步都反映了 Java 在持續(xù)改進(jìn)和適應(yīng)現(xiàn)代編程需求方面的承諾。
參考文檔
- https://openjdk.org/projects/jdk/
- JDK12: JDK12新功能深度解析_jdk12新特性-CSDN博客
- switch statement - What does the new keyword "yield" mean in Java 13? - Stack Overflow
- New Features in Java 13 | Baeldung
- Java 11 and 12 - New Features (packtpub.com)
- Java 16 and IntelliJ IDEA | The IntelliJ IDEA Blog (jetbrains.com)
- Sealed Class in Java - GeeksforGeeks