JDK9到JDK21中值得掌握的29個實用特性分享
Java的演進節(jié)奏從JDK9開始顯著加快,每半年一個新版本的發(fā)布節(jié)奏為Java帶來了大量的新特性。
其中包括令人矚目的虛擬線程、記錄類型等"明星特性",也有很多不太引人注意但同樣實用的小功能。
本文整理了29個JDK9到JDK21中值得掌握的實用特性,幫助你編寫更簡潔、高效、安全的Java代碼。
JDK 9 模塊化與API增強
1. 集合工廠方法:一行代碼創(chuàng)建不可變集合
在JDK9之前,創(chuàng)建小型不可變集合相當繁瑣,現(xiàn)在只需要一行代碼:
// 舊方式 List<String> list = Collections.unmodifiableList(Arrays.asList("Java", "Kotlin", "Scala")); Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<String, Integer>() {{ put("Java", 1995); put("Kotlin", 2011); put("Scala", 2004); }}); // JDK9方式 List<String> list = List.of("Java", "Kotlin", "Scala"); Set<String> set = Set.of("Java", "Kotlin", "Scala"); Map<String, Integer> map = Map.of( "Java", 1995, "Kotlin", 2011, "Scala", 2004 );
對于更多鍵值對,可以使用Map.ofEntries()
:
Map<String, Integer> largeMap = Map.ofEntries( Map.entry("Java", 1995), Map.entry("Kotlin", 2011), Map.entry("Scala", 2004), Map.entry("Groovy", 2003) // ...可以有更多entries );
實用場景:常量定義、配置集合、測試數(shù)據(jù)準備、API返回不可變結(jié)果。
2. 私有接口方法:接口代碼復(fù)用不再尷尬
JDK8引入了接口默認方法,JDK9進一步允許接口擁有私有方法,便于在接口內(nèi)部復(fù)用代碼:
public interface FileProcessor { // 公共抽象方法 void process(Path path); // 默認方法 default void processFile(String fileName) { validateFileName(fileName); // 復(fù)用私有方法進行驗證 process(Path.of(fileName)); log("Processed file: " + fileName); } default void processDirectory(String dirName) { validateFileName(dirName); // 復(fù)用相同的驗證邏輯 try (Stream<Path> paths = Files.list(Path.of(dirName))) { paths.forEach(this::process); } catch (IOException e) { handleException(e); // 復(fù)用私有方法處理異常 } log("Processed directory: " + dirName); } // 私有方法 - 供默認方法使用 private void validateFileName(String fileName) { if (fileName == null || fileName.isEmpty()) { throw new IllegalArgumentException("File name cannot be empty"); } } // 私有靜態(tài)方法 private static void log(String message) { System.out.println("[" + LocalDateTime.now() + "] " + message); } private void handleException(Exception e) { log("Error: " + e.getMessage()); } }
實用場景:API設(shè)計、接口默認方法邏輯復(fù)用、框架開發(fā)。
3. Stream API增強:流操作更靈活
JDK9為Stream API增加了幾個實用方法:
// 1. takeWhile - 從頭開始獲取元素,直到條件不滿足 Stream.of(2, 4, 6, 8, 9, 10, 12) .takeWhile(n -> n % 2 == 0) // 結(jié)果: [2, 4, 6, 8] .forEach(System.out::println); // 2. dropWhile - 從頭開始丟棄元素,直到條件不滿足 Stream.of(2, 4, 6, 8, 9, 10, 12) .dropWhile(n -> n % 2 == 0) // 結(jié)果: [9, 10, 12] .forEach(System.out::println); // 3. ofNullable - 安全創(chuàng)建單元素流,處理null值 Stream.ofNullable(null).count(); // 0 Stream.ofNullable("Java").count(); // 1 // 4. iterate方法重載 - 帶終止條件的迭代 // 舊方式需要使用limit或filter來限制 Stream.iterate(1, n -> n * 2) .limit(5) .forEach(System.out::println); // 新方式更直接 Stream.iterate(1, n -> n < 100, n -> n * 2) .forEach(System.out::println); // 1, 2, 4, 8, 16, 32, 64
實用場景:數(shù)據(jù)處理管道、復(fù)雜條件過濾、有界數(shù)據(jù)生成。
4. InputStream.transferTo():流復(fù)制不再繁瑣
在JDK9之前,在流之間復(fù)制數(shù)據(jù)需要手動處理緩沖區(qū),現(xiàn)在只需要一行代碼:
// 舊方式 - 冗長且易錯 try (InputStream is = new FileInputStream("source.txt"); OutputStream os = new FileOutputStream("target.txt")) { byte[] buffer = new byte[8192]; int length; while ((length = is.read(buffer)) > 0) { os.write(buffer, 0, length); } } // JDK9方式 - 簡潔明了 try (InputStream is = new FileInputStream("source.txt"); OutputStream os = new FileOutputStream("target.txt")) { is.transferTo(os); // 一行代碼搞定 }
實用場景:文件復(fù)制、網(wǎng)絡(luò)數(shù)據(jù)傳輸、流處理。
5. 改進的Process API:管理系統(tǒng)進程更容易
JDK9大幅增強了Process API,讓Java程序與系統(tǒng)進程的交互更加強大:
// 獲取當前進程 ProcessHandle current = ProcessHandle.current(); System.out.println("Current PID: " + current.pid()); // 獲取進程信息 current.info().user().ifPresent(user -> System.out.println("User: " + user)); current.info().commandLine().ifPresent(cmd -> System.out.println("Command: " + cmd)); current.info().startInstant().ifPresent(start -> System.out.println("Start time: " + start)); current.info().totalCpuDuration().ifPresent(cpu -> System.out.println("CPU time: " + cpu)); // 列出所有子進程 current.children().forEach(child -> System.out.println("Child PID: " + child.pid())); // 列出所有進程 ProcessHandle.allProcesses() .filter(ph -> ph.info().command().isPresent()) .forEach(ph -> System.out.println(ph.pid() + ": " + ph.info().command().get())); // 啟動并等待進程完成 ProcessBuilder pb = new ProcessBuilder("ls", "-l"); Process process = pb.start(); ProcessHandle handle = process.toHandle(); boolean terminated = handle.onExit().thenAccept(p -> System.out.println("Process " + p.pid() + " terminated") ).isDone();
實用場景:系統(tǒng)管理工具、守護進程、執(zhí)行外部命令、監(jiān)控應(yīng)用。
JDK 10 局部變量推斷
6. 局部變量類型推斷(var):告別冗長的變量聲明
JDK10引入了局部變量類型推斷,使用var
關(guān)鍵字讓編譯器推斷變量類型:
// 舊方式 - 類型重復(fù)且冗長 HashMap<String, List<Customer>> customersByCity = new HashMap<>(); BufferedReader reader = new BufferedReader(new FileReader("data.txt")); URLConnection connection = new URL("https://example.com").openConnection(); // JDK10方式 - 簡潔明了 var customersByCity = new HashMap<String, List<Customer>>(); var reader = new BufferedReader(new FileReader("data.txt")); var connection = new URL("https://example.com").openConnection(); // 在for循環(huán)中特別有用 for (var entry : customersByCity.entrySet()) { var city = entry.getKey(); var customers = entry.getValue(); // ... }
注意事項:
var
只能用于局部變量,不能用于字段、方法參數(shù)或返回類型- 聲明時必須初始化變量
- 不要過度使用,當類型不明顯時應(yīng)該明確聲明類型
最佳實踐:
// 好的用法 - 類型明確 var customers = new ArrayList<Customer>(); var entry = Map.entry("key", "value"); // 避免的用法 - 類型不明確 var result = getResult(); // 返回類型不明顯 var x = 1; // 基本類型推薦顯式聲明
實用場景:復(fù)雜泛型類型、匿名類、lambda表達式中的變量。
7. 不可修改集合的復(fù)制方法:集合轉(zhuǎn)換更安全
JDK10為集合框架增加了copyOf
方法,創(chuàng)建不可修改的集合副本:
// 原始集合 List<String> original = new ArrayList<>(List.of("Java", "Kotlin", "Scala")); Set<Integer> originalSet = new HashSet<>(Set.of(1, 2, 3)); Map<String, Integer> originalMap = new HashMap<>(Map.of("one", 1, "two", 2)); // 創(chuàng)建不可修改的副本 List<String> copy = List.copyOf(original); Set<Integer> copiedSet = Set.copyOf(originalSet); Map<String, Integer> copiedMap = Map.copyOf(originalMap); // 修改原集合不影響副本 original.add("Groovy"); System.out.println(original); // [Java, Kotlin, Scala, Groovy] System.out.println(copy); // [Java, Kotlin, Scala] // 嘗試修改副本會拋出異常 try { copy.add("Clojure"); // 拋出 UnsupportedOperationException } catch (UnsupportedOperationException e) { System.out.println("Cannot modify immutable copy"); }
與Collections.unmodifiableList()
不同,List.copyOf()
會創(chuàng)建一個全新的集合,如果原集合已經(jīng)是不可修改的,則可能直接返回原集合而不是副本。
實用場景:防御性編程、返回安全的集合副本、創(chuàng)建常量集合。
JDK 11 長期支持版功能增強
8. String新方法:文本處理得心應(yīng)手
JDK11為String類增加了幾個實用方法:
// 1. lines() - 按行分割字符串 String multiline = "Java\nKotlin\nScala"; multiline.lines() .map(String::toUpperCase) .forEach(System.out::println); // 2. strip(), stripLeading(), stripTrailing() - 去除空白字符 String text = " Hello World "; System.out.println(">" + text.strip() + "<"); // >Hello World< System.out.println(">" + text.stripLeading() + "<"); // >Hello World < System.out.println(">" + text.stripTrailing() + "<"); // > Hello World< // strip()與trim()的區(qū)別: strip()識別更多的Unicode空白字符 String unicodeWhitespace = "\u2005Hello\u2005"; System.out.println(">" + unicodeWhitespace.trim() + "<"); // >?Hello?< System.out.println(">" + unicodeWhitespace.strip() + "<"); // >Hello< // 3. isBlank() - 檢查字符串是否為空白 System.out.println(" ".isBlank()); // true System.out.println("".isBlank()); // true System.out.println(" a ".isBlank()); // false // 4. repeat() - 重復(fù)字符串 String star = "*"; System.out.println(star.repeat(10)); // ********** System.out.println("=".repeat(20)); // ====================
實用場景:處理用戶輸入、解析文本文件、構(gòu)建格式化輸出。
9. Files新方法:文件讀寫一步到位
JDK11為Files類添加了幾個便捷方法:
// 讀取文件為String String content = Files.readString(Path.of("config.json")); // 寫入String到文件 Files.writeString(Path.of("output.txt"), "Hello Java 11!"); // 使用指定編碼 String content = Files.readString(Path.of("data.txt"), StandardCharsets.UTF_8); Files.writeString(Path.of("log.txt"), "Logged at: " + LocalDateTime.now(), StandardCharsets.UTF_8); // 寫入字符串集合 List<String> lines = List.of("Line 1", "Line 2", "Line 3"); Files.write(Path.of("lines.txt"), lines);
實用場景:讀取配置文件、生成報告、日志記錄、快速文件I/O。
10. 標準HTTP客戶端:現(xiàn)代化網(wǎng)絡(luò)請求
JDK11將HTTP Client從孵化模塊升級為標準API,提供了現(xiàn)代化的HTTP客戶端:
// 創(chuàng)建HTTP客戶端 HttpClient client = HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .build(); // 構(gòu)建GET請求 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.github.com/users/octocat")) .header("User-Agent", "Java 11 HttpClient") .GET() .build(); // 同步發(fā)送請求,接收JSON響應(yīng) HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("Status code: " + response.statusCode()); System.out.println("Body: " + response.body()); // POST請求示例 HttpRequest postRequest = HttpRequest.newBuilder() .uri(URI.create("https://httpbin.org/post")) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{"name": "Java"}")) .build(); // 異步發(fā)送請求 client.sendAsync(postRequest, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println) .join(); // 處理JSON響應(yīng)(需要JSON庫) client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenApply(body -> { // 使用Jackson或Gson解析JSON return body; }) .thenAccept(System.out::println) .join();
實用場景:RESTful API調(diào)用、微服務(wù)通信、網(wǎng)絡(luò)爬蟲、數(shù)據(jù)集成。
JDK 12 語言和庫的改進
11. String.transform():鏈式字符串處理
JDK12為String類添加了transform
方法,支持鏈式函數(shù)轉(zhuǎn)換:
// 傳統(tǒng)方式 String original = "hello, world!"; String result = original.toUpperCase(); result = result.substring(0, 5); result = result + "..."; // 使用transform方式 String result = "hello, world!" .transform(String::toUpperCase) .transform(s -> s.substring(0, 5)) .transform(s -> s + "..."); System.out.println(result); // HELLO... // 復(fù)雜轉(zhuǎn)換 String parsed = "{ "name": "John", "age": 30 }" .transform(json -> { // 解析JSON // 此處簡化,實際應(yīng)使用Jackson等 return json.substring(json.indexOf("name") + 7, json.indexOf("age") - 3); }) .transform(String::trim) .transform(String::toUpperCase); System.out.println(parsed); // JOHN
對于多步轉(zhuǎn)換特別有用,提高了代碼可讀性。
實用場景:數(shù)據(jù)轉(zhuǎn)換鏈、復(fù)雜字符串處理、函數(shù)式數(shù)據(jù)處理管道。
12. Compact Number Formatting:數(shù)字的可讀性表示
JDK12引入了緊湊數(shù)字格式化功能,可以將大數(shù)字格式化為更易讀的形式:
// 創(chuàng)建簡短格式的格式化器 NumberFormat shortFormatter = NumberFormat.getCompactNumberInstance( Locale.US, NumberFormat.Style.SHORT); // 格式化數(shù)字 System.out.println(shortFormatter.format(1000)); // 1K System.out.println(shortFormatter.format(1500)); // 2K (四舍五入) System.out.println(shortFormatter.format(1000000)); // 1M System.out.println(shortFormatter.format(1000000000)); // 1B // 長格式 NumberFormat longFormatter = NumberFormat.getCompactNumberInstance( Locale.US, NumberFormat.Style.LONG); System.out.println(longFormatter.format(1000)); // 1 thousand System.out.println(longFormatter.format(1000000)); // 1 million // 其他語言的格式化 NumberFormat germanFormatter = NumberFormat.getCompactNumberInstance( Locale.GERMANY, NumberFormat.Style.SHORT); System.out.println(germanFormatter.format(1000)); // 1.000 NumberFormat chineseFormatter = NumberFormat.getCompactNumberInstance( Locale.CHINA, NumberFormat.Style.SHORT); System.out.println(chineseFormatter.format(1000)); // 1千 System.out.println(chineseFormatter.format(1000000)); // 100萬 // 自定義精度 shortFormatter.setMaximumFractionDigits(1); System.out.println(shortFormatter.format(1234)); // 1.2K System.out.println(shortFormatter.format(1500)); // 1.5K
實用場景:用戶界面顯示、儀表盤開發(fā)、數(shù)據(jù)可視化、國際化應(yīng)用。
JDK 14 友好錯誤信息與語言改進
13. 友好的NullPointerException:告別空指針調(diào)試噩夢
JDK14增強了NullPointerException,異常消息中會準確指出哪個變量是null:
// 假設(shè)有這樣的代碼 User user = null; String city = user.getAddress().getCity();
在JDK14之前,你會得到一個簡單的消息:
Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:5)
在JDK14及之后,異常消息變得非常具體:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "User.getAddress()" because "user" is null at Main.main(Main.java:5)
對于更復(fù)雜的表達式:
map.get("key").process().getNestedValue();
增強的NPE消息會明確指出哪一部分是null:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Result.getNestedValue()" because the return value of "ProcessedData.process()" is null
實用場景:調(diào)試復(fù)雜對象鏈、排查第三方庫錯誤、縮短問題定位時間。
14. Switch表達式:更簡潔的分支處理
JDK14正式發(fā)布了switch表達式(最初在JDK12引入為預(yù)覽特性):
// 傳統(tǒng)switch語句 String result; DayOfWeek day = LocalDate.now().getDayOfWeek(); switch (day) { case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: result = "Weekday"; break; case SATURDAY: case SUNDAY: result = "Weekend"; break; default: result = "Invalid day"; break; } // 新的switch表達式 String result = switch (day) { case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday"; case SATURDAY, SUNDAY -> "Weekend"; default -> "Invalid day"; }; // 復(fù)雜表達式,帶代碼塊 int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> { System.out.println("Six letters day"); yield 6; } case TUESDAY -> { System.out.println("Seven letters day"); yield 7; } case THURSDAY, SATURDAY -> { System.out.println("Eight letters day"); yield 8; } case WEDNESDAY -> { System.out.println("Nine letters day"); yield 9; } default -> { throw new IllegalStateException("Invalid day: " + day); } };
主要優(yōu)點:
- 可以作為表達式返回值
- 箭頭語法更簡潔
- 使用逗號可以合并多個case
- 不需要break語句,消除了常見的錯誤源
- 窮盡性檢查,確保所有情況都被處理
實用場景:狀態(tài)機實現(xiàn)、命令處理、配置解析、業(yè)務(wù)邏輯分派。
15. 記錄類(Records):數(shù)據(jù)類不再冗長
JDK14引入了Records作為預(yù)覽特性,在JDK16正式發(fā)布,為不可變數(shù)據(jù)類提供了簡潔的語法:
// 傳統(tǒng)POJO類 public final class Employee { private final String name; private final int id; private final Department department; public Employee(String name, int id, Department department) { this.name = name; this.id = id; this.department = department; } public String getName() { return name; } public int getId() { return id; } public Department getDepartment() { return department; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return id == employee.id && Objects.equals(name, employee.name) && Objects.equals(department, employee.department); } @Override public int hashCode() { return Objects.hash(name, id, department); } @Override public String toString() { return "Employee{" + "name='" + name + ''' + ", id=" + id + ", department=" + department + '}'; } } // 使用Record public record Employee(String name, int id, Department department) { }
Records自動生成構(gòu)造器、訪問器、equals/hashCode和toString方法。
你也可以向Record添加額外的構(gòu)造器、方法和靜態(tài)成員:
public record Point(int x, int y) { // 自定義緊湊構(gòu)造器 public Point { if (x < 0 || y < 0) { throw new IllegalArgumentException("Coordinates cannot be negative"); } } // 重載構(gòu)造器 public Point() { this(0, 0); } // 實例方法 public double distance(Point other) { return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2)); } // 靜態(tài)成員 public static final Point ORIGIN = new Point(0, 0); // 靜態(tài)方法 public static Point of(int x, int y) { return new Point(x, y); } }
實用場景:DTO對象、API響應(yīng)模型、消息體、不可變數(shù)據(jù)容器、值對象。
JDK 15-16 文本和類型檢查優(yōu)化
16. 文本塊:多行字符串不再痛苦
JDK15正式發(fā)布了文本塊功能(在JDK13首次預(yù)覽),讓多行字符串變得簡單優(yōu)雅:
// 傳統(tǒng)多行字符串 String json = "{\n" + " "name": "John Doe",\n" + " "age": 30,\n" + " "address": {\n" + " "street": "123 Main St",\n" + " "city": "Anytown"\n" + " }\n" + "}"; // 使用文本塊 String json = """ { "name": "John Doe", "age": 30, "address": { "street": "123 Main St", "city": "Anytown" } } """; // HTML示例 String html = """ <html> <body> <h1>Hello, World!</h1> </body> </html> """; // SQL查詢 String query = """ SELECT id, first_name, last_name FROM employees WHERE department_id = ? ORDER BY last_name, first_name """;
文本塊還支持字符串插值和格式控制:
// 使用\避免行尾換行 String compact = """ <html>\ <body>\ <p>Hello</p>\ </body>\ </html>\ """; // 與String::formatted配合使用 String template = """ Dear %s, Your order #%d has been shipped on %s. Thank you, Customer Service """; String message = template.formatted("John", 12345, "2023-05-15");
實用場景:SQL查詢、HTML/JSON/XML模板、代碼生成、多行文本配置。
17. instanceof模式匹配:類型檢查與轉(zhuǎn)換合二為一
JDK16正式發(fā)布的instanceof模式匹配簡化了類型檢查和轉(zhuǎn)換:
// 傳統(tǒng)方式 if (obj instanceof String) { String s = (String) obj; if (s.length() > 5) { System.out.println(s.toUpperCase()); } } // 使用模式匹配 if (obj instanceof String s && s.length() > 5) { System.out.println(s.toUpperCase()); } // 在復(fù)雜條件中使用 if (obj instanceof String s && s.length() > 10 || obj instanceof List<?> list && list.size() > 5) { // 使用s或list } // 與switch配合使用(JDK17預(yù)覽特性) Object value = getValue(); switch (value) { case String s when s.length() > 5 -> System.out.println("Long string: " + s); case String s -> System.out.println("Short string: " + s); case List<?> l -> System.out.println("List with " + l.size() + " elements"); default -> System.out.println("Unknown type"); }
實用場景:多態(tài)對象處理、類型安全轉(zhuǎn)換、空檢查簡化。
18. 外部內(nèi)存訪問API (Foreign Memory Access):安全高效的本地內(nèi)存操作
JDK16引入了Foreign Memory Access API(孵化器階段),為Java提供了安全高效的本地內(nèi)存訪問能力:
// 分配堆外內(nèi)存 try (Arena arena = Arena.ofConfined()) { // 分配100字節(jié)的本地內(nèi)存 MemorySegment segment = arena.allocate(100); // 寫入數(shù)據(jù) MemorySegment.copy(new byte[] {1, 2, 3, 4, 5}, 0, segment, 0, 5); // 讀取數(shù)據(jù) byte value = segment.get(ValueLayout.JAVA_BYTE, 2); // 讀取索引2的值 System.out.println("Value at index 2: " + value); // 輸出 3 // 填充內(nèi)存段 MemorySegment.fill(segment, (byte) 10); // 使用VarHandle操作內(nèi)存 VarHandle intHandle = ValueLayout.JAVA_INT.varHandle(); intHandle.set(segment, 0, 42); int result = (int) intHandle.get(segment, 0); System.out.println("Integer value: " + result); // 輸出 42 // 內(nèi)存地址操作 long address = segment.address().toRawLongValue(); System.out.println("Memory address: 0x" + Long.toHexString(address)); }
這個API在JDK17中得到了改進,在JDK21中正式發(fā)布。它為需要處理大量數(shù)據(jù)的應(yīng)用程序提供了比ByteBuffer更強大的替代方案。
實用場景:高性能計算、大數(shù)據(jù)處理、網(wǎng)絡(luò)應(yīng)用、與本地庫集成。
JDK 17 長期支持版的強大特性
19. 密封類(Sealed Classes):精確控制繼承關(guān)系
JDK17正式發(fā)布的密封類允許更精確地控制哪些類可以繼承一個類:
// 聲明一個密封接口,只允許特定的類實現(xiàn)它 public sealed interface Shape permits Circle, Rectangle, Triangle { double area(); } // 最終實現(xiàn)類,不允許進一步繼承 public final class Circle implements Shape { private final double radius; public Circle(double radius) { this.radius = radius; } @Override public double area() { return Math.PI * radius * radius; } } // 允許進一步繼承的類 public non-sealed class Rectangle implements Shape { private final double width; private final double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } @Override public double area() { return width * height; } } // 限制繼承的類 public sealed class Triangle implements Shape permits EquilateralTriangle, RightTriangle { // ...實現(xiàn)... } // 允許的子類 public final class EquilateralTriangle extends Triangle { // ...實現(xiàn)... } public final class RightTriangle extends Triangle { // ...實現(xiàn)... }
密封類與switch模式匹配和記錄類結(jié)合使用時特別強大,編譯器可以進行窮盡性檢查:
double calculateArea(Shape shape) { return switch (shape) { case Circle c -> Math.PI * c.radius() * c.radius(); case Rectangle r -> r.width() * r.height(); case Triangle t -> t.base() * t.height() / 2; // 不需要default分支,因為編譯器知道這些是Shape的所有可能實現(xiàn) }; }
實用場景:領(lǐng)域模型設(shè)計、類型安全的API、狀態(tài)機實現(xiàn)、編譯器檢查增強。
20. 增強的偽隨機數(shù)生成器:更靈活、可預(yù)測的隨機數(shù)
JDK17引入了增強的偽隨機數(shù)生成器(PRNG)框架,提供了更多算法和更好的接口:
// 獲取默認的隨機數(shù)生成器 RandomGenerator random = RandomGenerator.getDefault(); System.out.println(random.nextInt(100)); // 0-99之間的隨機數(shù) // 使用特定算法的生成器 RandomGenerator xoroshiro = RandomGenerator.of("Xoroshiro128PlusPlus"); System.out.println(xoroshiro.nextLong()); // 使用L32X64MixRandom - 平衡了速度和質(zhì)量的算法 RandomGenerator fastRandom = RandomGenerator.of("L32X64MixRandom"); for (int i = 0; i < 5; i++) { System.out.println(fastRandom.nextInt(1000)); } // 創(chuàng)建可復(fù)現(xiàn)的隨機數(shù)序列 (使用相同的種子) RandomGenerator seeded = RandomGenerator.of("Xoshiro256PlusPlus"); ((SplittableRandomGenerator) seeded).setSeed(42); for (int i = 0; i < 5; i++) { System.out.println(seeded.nextInt(100)); } // 生成隨機流 DoubleStream randomDoubles = RandomGenerator.getDefault().doubles(1000); randomDoubles.forEach(System.out::println); // 查看所有可用的算法 RandomGenerator.all() .map(provider -> provider.name() + ": " + provider.group()) .sorted() .forEach(System.out::println);
實用場景:科學(xué)計算、模擬、游戲開發(fā)、測試數(shù)據(jù)生成、加密應(yīng)用。
21. 向量API (Incubator):性能密集型計算
JDK17引入了孵化器階段的向量API,支持SIMD(單指令多數(shù)據(jù))風(fēng)格的操作,顯著加速特定類型的計算:
// 使用IntVector加速數(shù)組求和 static int sumArrayVectorized(int[] a) { var species = IntVector.SPECIES_PREFERRED; var sum = IntVector.zero(species); var i = 0; // 處理可以向量化的部分 for (; i <= a.length - species.length(); i += species.length()) { var v = IntVector.fromArray(species, a, i); sum = sum.add(v); } // 處理剩余元素 var result = sum.reduceLanes(VectorOperators.ADD); for (; i < a.length; i++) { result += a[i]; } return result; } // 向量化矩陣乘法 static void multiplyMatricesVectorized(float[] a, float[] b, float[] c, int n) { var species = FloatVector.SPECIES_PREFERRED; int limit = species.length(); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { var sum = FloatVector.zero(species); int k = 0; // 使用向量計算 for (; k <= n - limit; k += limit) { var va = FloatVector.fromArray(species, a, i * n + k); var vb = FloatVector.fromArray(species, b, k * n + j); sum = sum.add(va.mul(vb)); } // 累加向量結(jié)果 float dotProduct = sum.reduceLanes(VectorOperators.ADD); // 處理剩余元素 for (; k < n; k++) { dotProduct += a[i * n + k] * b[k * n + j]; } c[i * n + j] = dotProduct; } } }
JDK19和JDK21繼續(xù)改進了向量API,但截至JDK21仍處于孵化器階段。
實用場景:科學(xué)計算、圖像處理、機器學(xué)習(xí)、信號處理、金融模擬。
JDK 18-19 工具鏈和API增強
22. 簡單Web服務(wù)器:快速啟動靜態(tài)文件服務(wù)
JDK18引入了一個簡單的命令行HTTP服務(wù)器,可以快速啟動靜態(tài)文件服務(wù):
// 命令行用法 // jwebserver -p 8000 -d /path/to/directory // 在代碼中使用: import com.sun.net.httpserver.*; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.file.Path; public class SimpleFileServer { public static void main(String[] args) throws IOException { var server = SimpleFileServer.createFileServer( new InetSocketAddress(8000), Path.of("/path/to/directory"), SimpleFileServer.OutputLevel.VERBOSE ); server.start(); System.out.println("Server started at http://localhost:8000"); } }
這個功能特別適合開發(fā)和測試環(huán)境,比如快速預(yù)覽靜態(tài)網(wǎng)站或測試API調(diào)用。
實用場景:前端開發(fā)、靜態(tài)網(wǎng)站預(yù)覽、本地測試環(huán)境、原型開發(fā)。
23. 代碼片段in JavaDoc:更好的API文檔
JDK18引入了@snippet
標簽,允許在JavaDoc中添加帶有語法高亮的代碼示例:
/** * This class provides utility methods for string operations. * * <p>Example usage:</p> * {@snippet : * String result = StringUtils.capitalize("hello"); // Returns "Hello" * boolean isEmpty = StringUtils.isBlank(" "); // Returns true * } */ public class StringUtils { // 類實現(xiàn)省略 }
增強的文檔還支持高亮、區(qū)域標記和錯誤標記:
/** * Example of snippet with highlighting: * {@snippet : * // @highlight region="important" * String encoded = Base64.getEncoder().encodeToString(data); * // @end * * // @highlight regex="data" type="bold" * byte[] decoded = Base64.getDecoder().decode(encoded); * * // @replace regex="badPractice()" replacement="goodPractice()" type="error" * result = badPractice(); * } */
實用場景:API文檔、開源項目、技術(shù)指南、教程編寫。
24. 外部函數(shù)接口 (Foreign Function & Memory API)
JDK19改進了孵化器中的外部函數(shù)接口(在JDK21中正式發(fā)布),讓Java與本地代碼交互更加簡單:
// 定義C庫函數(shù)的接口 import java.lang.foreign.*; import static java.lang.foreign.ValueLayout.*; public class LibCDemo { public static void main(String[] args) { // 獲取C標準庫的鏈接器 Linker linker = Linker.nativeLinker(); // 查找printf函數(shù) SymbolLookup stdlib = linker.defaultLookup(); MethodHandle printf = stdlib.find("printf") .map(addr -> linker.downcallHandle( addr, FunctionDescriptor.of(JAVA_INT, ADDRESS), Linker.Option.firstVariadicArg(1) )) .orElseThrow(); // 準備字符串參數(shù) try (Arena arena = Arena.ofConfined()) { MemorySegment cString = arena.allocateUtf8String("Hello from Java! Count: %d\n"); // 調(diào)用printf try { printf.invoke(cString, 42); } catch (Throwable e) { e.printStackTrace(); } } } }
這個API消除了JNI的大部分復(fù)雜性,提供了更安全、更簡潔的方式來調(diào)用本地代碼。
實用場景:與C/C++庫集成、系統(tǒng)編程、性能關(guān)鍵應(yīng)用、多語言項目。
JDK 20-21 現(xiàn)代并發(fā)和語言增強
25. 虛擬線程(Virtual Threads):并發(fā)革命
JDK21正式發(fā)布了虛擬線程,這是Java并發(fā)編程的重大變革:
// 創(chuàng)建和啟動單個虛擬線程 Thread.startVirtualThread(() -> { System.out.println("Running in virtual thread"); }); // 使用虛擬線程運行多個任務(wù) try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { // 提交1000個任務(wù),每個在獨立的虛擬線程中運行 for (int i = 0; i < 1000; i++) { int taskId = i; executor.submit(() -> { System.out.println("Task " + taskId + " running on " + Thread.currentThread()); // 模擬IO操作 try { Thread.sleep(Duration.ofMillis(100)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return taskId; }); } } // 自動關(guān)閉executor // 以構(gòu)建器方式創(chuàng)建虛擬線程 ThreadFactory factory = Thread.ofVirtual().name("worker-", 0).factory(); Thread worker = factory.newThread(() -> { // 任務(wù)代碼 }); worker.start(); // 使用虛擬線程改寫傳統(tǒng)的阻塞式IO代碼 void processFile(Path file) throws IOException { // 這段代碼在虛擬線程中運行時不會阻塞平臺線程 try (var reader = Files.newBufferedReader(file)) { String line; while ((line = reader.readLine()) != null) { processLine(line); } } }
虛擬線程的主要優(yōu)勢是可以創(chuàng)建數(shù)百萬個輕量級線程,而不會耗盡系統(tǒng)資源。它們特別適合IO密集型應(yīng)用,使同步代碼可以獲得與異步代碼相當?shù)臄U展性。
實用場景:高并發(fā)Web服務(wù)器、微服務(wù)、數(shù)據(jù)處理管道、爬蟲程序。
26. 結(jié)構(gòu)化并發(fā)(Structured Concurrency)
可以管理異步任務(wù)的生命周期
JDK21引入了結(jié)構(gòu)化并發(fā)API(預(yù)覽特性),簡化了多線程代碼的錯誤處理和資源管理:
// 并行獲取用戶及其訂單 record User(int id, String name) {} record Order(int id, double amount) {} record UserWithOrders(User user, List<Order> orders) {} UserWithOrders getUserWithOrders(int userId) { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { // 并行執(zhí)行兩個子任務(wù) Future<User> userFuture = scope.fork(() -> fetchUser(userId)); Future<List<Order>> ordersFuture = scope.fork(() -> fetchOrders(userId)); // 等待所有任務(wù)完成 scope.join(); // 檢查子任務(wù)是否有異常 scope.throwIfFailed(e -> new RuntimeException("Failed to fetch data", e)); // 獲取結(jié)果 return new UserWithOrders(userFuture.resultNow(), ordersFuture.resultNow()); } catch (Exception e) { throw new RuntimeException(e); } } // 使用結(jié)構(gòu)化并發(fā)處理多個API調(diào)用 try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { var weatherFuture = scope.fork(() -> callWeatherAPI()); var trafficFuture = scope.fork(() -> callTrafficAPI()); var newsFuture = scope.fork(() -> callNewsAPI()); try { scope.join(); scope.throwIfFailed(); // 所有API調(diào)用成功,處理結(jié)果 var dashboard = createDashboard( weatherFuture.resultNow(), trafficFuture.resultNow(), newsFuture.resultNow() ); return dashboard; } catch (Exception e) { // 有一個API調(diào)用失敗,所有任務(wù)都被取消 return createFallbackDashboard(); } }
結(jié)構(gòu)化并發(fā)確保所有子任務(wù)在父任務(wù)退出前要么完成,要么被取消,避免了資源泄漏和"遺忘"的后臺任務(wù)。
實用場景:API聚合、微服務(wù)通信、并行數(shù)據(jù)處理、復(fù)雜異步工作流。
27. Record模式(Record Patterns):解構(gòu)數(shù)據(jù)更簡單
JDK21正式發(fā)布的Record模式允許在模式匹配中解構(gòu)記錄:
// 定義一些記錄 record Point(int x, int y) {} record Rectangle(Point topLeft, Point bottomRight) {} record Circle(Point center, int radius) {} // 使用傳統(tǒng)方式處理記錄 Object shape = new Rectangle(new Point(1, 2), new Point(5, 6)); if (shape instanceof Rectangle) { Rectangle r = (Rectangle) shape; Point topLeft = r.topLeft(); Point bottomRight = r.bottomRight(); int width = bottomRight.x() - topLeft.x(); int height = bottomRight.y() - topLeft.y(); System.out.println("Rectangle with width " + width + " and height " + height); } // 使用Record模式解構(gòu) if (shape instanceof Rectangle(Point(var x1, var y1), Point(var x2, var y2))) { int width = x2 - x1; int height = y2 - y1; System.out.println("Rectangle with width " + width + " and height " + height); } // 結(jié)合switch使用 String getDescription(Object shape) { return switch (shape) { case Rectangle(Point(var x1, var y1), Point(var x2, var y2)) -> "Rectangle from (%d,%d) to (%d,%d)".formatted(x1, y1, x2, y2); case Circle(Point(var x, var y), var r) -> "Circle at (%d,%d) with radius %d".formatted(x, y, r); default -> "Unknown shape"; }; }
Record模式與嵌套模式結(jié)合使用時特別強大,可以輕松處理復(fù)雜的數(shù)據(jù)結(jié)構(gòu)。
實用場景:數(shù)據(jù)轉(zhuǎn)換、JSON/XML解析結(jié)果處理、領(lǐng)域模型操作、事件處理。
28. 字符串模板(String Templates):安全高效的字符串插值
JDK21引入了字符串模板作為預(yù)覽特性,提供比字符串連接更簡潔、更安全的方式:
// 傳統(tǒng)方式 String name = "Alice"; int age = 30; String message = "Hello, " + name + "! Next year, you'll be " + (age + 1) + "."; // 使用字符串模板 String message = STR."Hello, {name}! Next year, you'll be {age + 1}."; // 帶表達式的模板 String status = "active"; String message = STR."User status: {status.toUpperCase()} (set {LocalDate.now()})"; // 格式化數(shù)字 double value = 1234.56789; String formatted = STR."The value is {value%.2f}"; // "The value is 1234.57" // 多行JSON String json = STR.""" { "name": "{name}", "age": {age}, "isAdult": {age >= 18}, "contacts": [ {generateContactsJson()} ] } """;
字符串模板不僅語法簡潔,還提供了類型安全和對表達式的編譯時檢查。
實用場景:生成JSON/XML/HTML、日志記錄、消息格式化、SQL語句構(gòu)建。
29. 序列集合 (Sequenced Collections):統(tǒng)一的集合操作
JDK21引入了SequencedCollection
、SequencedSet
和SequencedMap
接口,為Java集合框架添加了方向性和順序性操作:
// 序列化集合基本用法 SequencedCollection<String> names = new ArrayList<>(List.of("Alice", "Bob", "Charlie")); // 獲取第一個和最后一個元素 String first = names.getFirst(); // "Alice" String last = names.getLast(); // "Charlie" // 添加元素到兩端 names.addFirst("Zoe"); names.addLast("David"); System.out.println(names); // [Zoe, Alice, Bob, Charlie, David] // 創(chuàng)建反向視圖 SequencedCollection<String> reversed = names.reversed(); System.out.println(reversed); // [David, Charlie, Bob, Alice, Zoe] // 序列化Map SequencedMap<String, Integer> scores = new LinkedHashMap<>(); scores.put("Alice", 95); scores.put("Bob", 85); scores.put("Charlie", 90); // 獲取第一個和最后一個條目 Map.Entry<String, Integer> firstEntry = scores.firstEntry(); // Alice=95 Map.Entry<String, Integer> lastEntry = scores.lastEntry(); // Charlie=90 // 添加條目到兩端 scores.putFirst("Zoe", 100); scores.putLast("David", 80); // 獲取鍵或值的序列化視圖 SequencedCollection<String> keys = scores.sequencedKeySet(); SequencedCollection<Integer> values = scores.sequencedValues();
這些接口統(tǒng)一了Java集合框架中的順序操作,使API更加一致?,F(xiàn)有的有序集合類如ArrayList
、LinkedHashSet
和LinkedHashMap
都實現(xiàn)了這些新接口。
實用場景:維持插入順序的集合、FIFO/LIFO隊列操作、雙向迭代、有序數(shù)據(jù)處理。
總結(jié)
Java語言在保持向后兼容性的同時,不斷引入新特性以提高開發(fā)效率和代碼質(zhì)量。掌握這些新特性不僅能提高開發(fā)效率,還能編寫出更加簡潔、健壯的代碼。
到此這篇關(guān)于JDK9到JDK21中值得掌握的29個實用特性分享的文章就介紹到這了,更多相關(guān)JDK實用特性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java基礎(chǔ)之Math和Random類知識總結(jié)
今天帶大家來學(xué)習(xí)java的Math和Random類,文中有非常詳細的代碼示例及介紹,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們很有幫助喲,需要的朋友可以參考下2021-05-05Java實現(xiàn)文件或文件夾的復(fù)制到指定目錄實例
本篇文章主要介紹了Java實現(xiàn)文件或文件夾的復(fù)制到指定目錄實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03IDEA 集成 Docker 插件一鍵部署 SpringBoot 應(yīng)用
通過本文介紹的方法,我們期望能幫助開發(fā)者更輕松地在IDEA中實現(xiàn)Spring Boot應(yīng)用的Docker化部署,為現(xiàn)代軟件開發(fā)提供更便捷的解決方案,感興趣的朋友一起看看吧2023-11-11SpringBoot項目創(chuàng)建使用+配置文件+日志文件詳解
Spring的出現(xiàn)是為了簡化 Java 程序開發(fā),而 SpringBoot 的出現(xiàn)是為了簡化 Spring 程序開發(fā),這篇文章主要介紹了SpringBoot項目創(chuàng)建使用+配置文件+日志文件,需要的朋友可以參考下2023-02-02SpringBoot整合jasypt實現(xiàn)重要數(shù)據(jù)加密
Jasypt是一個專注于簡化Java加密操作的開源工具,這篇文章主要為大家介紹了詳細介紹了如何使用jasypt實現(xiàn)重要數(shù)據(jù)加密,感興趣的小伙伴可以了解下2025-03-03