public class Demo{ public static void main(String[] args){ var score = 'C'; // 執行switch分支語句 String s = switch (score){ case 'A', 'B' -> "上等"; case 'C' -> "中等"; case 'D', 'E' -> "下等"; default -> { if (score > 100) { yield "數據不能超過100"; } else { yield score + "此分數低于0分"; } } } }}
JMH ,即 Java Microbenchmark Harness ,是專門用于代碼微基準測試的工具套件。
<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>
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()) :這個方法表示你想要運行哪個類的基準測試。//exclude("xxx") :這個方法表示你想要在基準測試中排除哪個方法。//forks(1) :這個方法表示你想要進行多少輪的基準測試。每一輪測試都會在一個新的 JVM 進程中進行,以確保每輪測試的環境是獨立的。//warmupIterations(5) :這個方法表示你想要進行多少次預熱迭代。預熱迭代是為了讓 JVM 達到穩定狀態,預熱迭代的結果不會被計入最終的基準測試結果。//measurementIterations(5) :這個方法表示你想要進行多少次正式的基準測試迭代,這些迭代的結果會被用來計算基準測試的最終結果。
圖片
圖片
對應 Mode 選項,可用于類或者方法上,需要注意的是,這個注解的 value 是一個數組,可以把幾種 Mode 集合在一起執行,還可以設置為 Mode.All ,即全部執行一遍。
圖片
類注解,JMH 測試類必須使用 @State 注解,State 定義了一個類實例的生命周期,可以類比 SpringBean 的 Scope 。由于 JMH 允許多線程同時執行測試,不同的選項含義如下:
Scope.Thread :默認的 State ,每個測試線程分配一個實例。
Scope.Benchmark :所有測試線程共享一個實例,用于測試有狀態實例在多線程共享下的性能。
Scope.Group :每個線程組共享一個實例。
如果你想測試一個對象在多線程環境下的行為,你可以選擇 Scope.Benchmark 。如果你想要每個線程都有自己的狀態,你可以選擇 Scope.Thread 。如果你想要在同一線程組內的所有線程共享狀態,你可以選擇 Scope.Group 。
benchmark 結果所使用的時間單位,可用于類或者方法注解,使用 java.util.concurrent.TimeUnit 中的標準時間單位。
方法注解,表示該方法是需要進行 benchmark 的對象。
背景:在同一個物理機上啟動多個 JVM 時,如果每個虛擬機都單獨裝載自己需要的所有類,啟動成本和內存占用是比較高的。所以引入了類數據共享機制 ( Class Data Sharing ,簡稱 CDS ) 的概念,通過把一些核心類在每個 JVM 間共享,每個 JVM 只需要裝載自己的應用類即可。
JDK12 之前想要利用 CDS 的用戶,必須 -Xshare:dump 作為額外的步驟來運行。
JDK12 針對 64 位平臺使其默認生成類數據共享 ( CDS ) 歸檔。
好處:JVM啟動時間減少了。因為核心類是共享的,所以 JVM 的內存占用也減少了。
JDK13 則支持在應用運行之后進行動態歸檔。需要使用 -XX:ArchiveClassesAtExit=filename.jsa 選項來指定一個文件,JVM 會在退出時將應用程序類和標準庫類的元數據寫入這個文件。然后,在下一次啟動 JVM 時,你可以使用 -XX:SharedArchiveFile=filename.jsa 選項來指定剛才創建的文件。
好處:這個特性允許 JVM 在運行時捕獲類的元數據,然后在下一次 JVM 啟動時重用這些元數據,從而提高啟動速度和減少內存占用。
添加一個名為 Shenandoah 的新垃圾收集算法,通過與正在運行的 Java 線程同時進行疏散工作來減少 GC 暫停時間,最終目標旨在針對 JVM 上的內存收回實現低停頓的需求。
Shenandoah 是以實驗特性在 JDK12 中引入的。在 JDK15 中正式上線。
使用 Shenandoah 的暫停時間與堆大小無關,這意味著無論堆是 200MB 還是 200GB ,都將具有相同的一致暫停時間。與 ZGC 類似,Shenandoah GC 主要目標是 99.9% 的暫停小于 10ms ,暫停與堆大小無關等。
ZGC 和 ShenandoahGC 的一些主要區別:
使用方法:要啟用/使用 Shenandoah GC,需要以下 JVM 選項: -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC。作為實驗性功能,Shenandoah 構建系統會自動禁用不受支持的配置。
var rs = "test".transform(s -> s + "Java").transform(s -> s.toUpperCase()); // TESTJAVA
String result = "Java/njava/ntest".indent(3);/*結果會縮進三格 Java java test*/
返回內容第一次不匹配的字符位置索引。
System.out.println(Files.mismatch(Path.of("a.txt"),Path.of("b.txt")));
NumberFormat 添加了對 ”緊湊形式格式化數字“ 的支持。
”緊湊數字格式“是指以簡短或人類可讀形式表示的數字。
例如,在 en_US 語言環境中,1000 可以格式化為 “ 1K ”,1000000 可以格式化為 “ 1M ”,具體取決于指定的樣式 NumberFormat.Style 。緊湊數字格式由 LDML 的 Compact Number 格式規范定義。要獲取實例,請使用 NumberFormat 緊湊數字格式所給出的工廠方法之一。
NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);String result = fmt.format(1000);//1Kvar cnf = NumberFormat.getCompactNumberInstance(Locale.CHINA,NumberFormat.Style.SHORT);System.out.println(cnf.format(5_0000));//"5萬"System.out.println(cnf.format(7_9200));//"7.9萬"System.out.println(cnf.format(8_000_000));//"800萬"System.out.println(cnf.format(9L << 30));//"96億"System.out.println(cnf.format(6L << 50));//"5637142兆"System.out.println(cnf.format(6L << 60));//"6917529京"
JDK12 支持 Unicode11.0
JDK13 支持 Unicode12.1
從 JDK14 到 JDK17 均是支持 Unicode13.0
JDK14 之前,從報錯中我們只能得到錯誤出現的行數,但在 JDK14 之后,會清晰的告訴你哪個對象空指針了。
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.charAt(int)" because "str" is nullat com.qf.jdk14.Test.main(Test.java:11)
背景:在 Java 中,在字符串文字中嵌入 HTML ,XML ,SQL 或 JSON 片段通常需要先進行轉義和串聯的大量編輯,然后才能編譯包含該片段的代碼。該代碼段通常難以閱讀且難以維護。
Java 的文本塊特性是在 JDK15 中正式實現的。這個特性首先在 JDK13 中以預覽版的形式發布,然后在 JDK14 中改進并再次以預覽版的形式發布。這一特性提高了 Java 程序書寫大段字符串文本的可讀性和方便性。
文本塊的開頭定界符是由三個雙引號 """ 開始,從新的一行開始字符串的內容,以 """ 結束。如果結束的 """ 另起一行時,字符串內容最后會留有一新行。
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`/n" + "WHERE `CITY` = 'INDIANAPOLIS'/n" + "ORDER BY `ID`, `LAST_NAME`;";//使用文本塊語法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";//使用文本塊語法String html = """ <html> <body> <p>Hello, world</p> </body> </html> """;
Java 編譯器會自動刪除不需要的縮進:
System.out.println(""" Hello, multiline text blocks! """);// 結果// > Hello,// > multiline// > text blocks!
背景:現在已有的 java.net.Socket 和 java.net.ServerSocket 以及它們的實現類,都可以回溯到 JDK1.0 時代了。原始 socket 的維護和調試都很痛苦。實現類還使用了線程棧作為 I/O 的緩沖,導致在某些情況下還需要增加線程棧的大小。該實現還存在幾個并發問題,需要徹底解決。在未來的網絡世界,要快速響應,不能阻塞本地方法線程,當前的實現不適合使用了。
JDK13 全新實現的 NioSocketImpl 來替換 JDK1 的 SocketImpl 和 PlainSocketImpl。
通常我們在使用大型的框架或者 lambda 表達式的時候,會動態生成很多類。但是不幸的是標準的定義類的API:ClassLoader::defineClass 和 Lookup::defineClass 不能夠區分出這些類是動態生成(運行時生成)的還是靜態生成(編譯生成)的。
一般來說動態生成的類生命周期更短,并且其可?性要更低。但是現有的 JDK 并沒有這個功能。
所有有了 HiddenClasses 的提案,通過 HiddenClasses ,不管是 JDK 還是 JDK 外部的框架,在生成動態類的時候都可以定義為 HiddenClasses,這樣可以更加有效的控制這些動態生成類的生命周期和可?性。
instanceof關鍵詞主要用來判斷某個對象是不是某個類的實例。
比如,有時候我們要處理一個類似這樣的數據集:
Map<String, Object> data = new HashMap<>();data.put("test", "111");data.put("test2", 222);
JDK16 之前需要先判斷獲取的 value 是否是 String ,再做強制類型轉換:
Object value = data.get("test");if (value instanceof String){ String s = (String) value; System.out.println(s.substring(1));}
在 JDK16 的增強之后,對于 instanceof 的判斷以及類型轉換可以合二為一了:
Object value = data.get("test");if (value instanceof String s){ System.out.println(s.substring(1));}
Records 的目標是擴展 Java 語言語法,Records 為聲明類提供了一種緊湊的語法,通過對類做這樣的聲明,編譯器可以通過自動創建所有方法并讓所有字段參與 hashCode() 等方法。其目的是為了充當不可變數據的透明載體的類。
舊方法定義實體類,代碼如下:
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); }}
通過 Record 類方式,一句話就可以實現以上功能,代碼如下:
public record User(String username, String password) {}
在 JDK16 之前的版本中,我們不能在類名后面直接寫參數來定義類的狀態。這是 JDK16 引入 record 類的一個新特性。
調用 Record 類方式,如下:
public class App { public static void main(String[] args) { User user = new User("user", "123456"); System.out.println(user.username()); }}
注意事項:
在 JDK15 中,Java 提出了密封類( Sealed Classes )的概念,在 JDK17 中被正式確認。密封類允許類和接口定義其允許的子類型。因此,如果一個類沒有顯式地使用 sealed 、non-sealed 或 final 關鍵字,那么它的默認權限就是 non-sealed 。
以下是一個密封類的代碼示例:
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"); }}
在這個例子中,Human 是一個密封類,它只允許 Kyrie,LeBron 和 George 這三個類繼承。這樣,我們就可以更精確地控制哪些類可以繼承 Human 類,從而提高代碼的安全性和可讀性。
在 JDK17 中,引入了一項新特性:統一日志異步刷新。
先將日志寫入緩存,然后再異步地刷新到日志文件,這樣寫日志的操作就不會阻塞執行業務邏輯的線程,從而提高了程序的運行效率。這個特性對于需要大量日志輸出,并且對性能有較高要求的應用來說,是一個非常實用的改進。可以通過傳遞命令行選項 -Xlog:async 來開啟此功能。
從 JDK11 到 JDK17 ,Java 的發展經歷了一系列重要的里程碑。其中最重要的是 JDK17 的發布,這是一個長期支持(LTS)版本,它將獲得長期的更新和支持,有助于保持程序的穩定性和可靠性。此外,Java 的性能也有了顯著的提升。這些進步都反映了 Java 在持續改進和適應現代編程需求方面的承諾。
本文鏈接:http://www.www897cc.com/showinfo-26-68322-0.htmlJDK17 與 JDK11 特性差異淺談
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com