大家好!今天我要和大家分享 Java 9 中那些真正改變我們編碼方式的新特性。作為 Java 開發者,瞭解這些新功能不僅能讓你的代碼更簡潔、更高效,還能幫助你在團隊中脱穎而出。
Java 9 於 2017 年 9 月發佈,它帶來了自 Java 8 以來最重大的架構變革。與 Java 8 注重語法層面的革新(如 Lambda 表達式)不同,Java 9 更關注基礎設施和平台級別的改進,為大型應用開發、微服務架構和模塊化編程提供了強大支持。
廢話不多説,讓我們直接進入正題!
1. 模塊系統(Project Jigsaw)
Java 9 最重要的變革無疑是引入了模塊系統,這是 Java 平台級別的重大架構改變。
什麼是模塊系統?
模塊系統允許我們將代碼組織成更高級別的組件(稱為模塊),每個模塊都明確聲明自己的依賴關係和對外公開的 API。
使用案例
讓我們創建一個簡單的模塊化應用程序:
// 在module-info.java文件中定義模塊
module com.example.myapp {
requires java.base; // 隱式依賴,可省略
requires java.logging;
requires transitive java.sql; // 傳遞依賴,使用此模塊的模塊也能訪問java.sql
exports com.example.myapp.api; // 只導出API包
opens com.example.myapp.model to java.persistence; // 僅向特定模塊開放反射訪問
}
// com.example.myapp.api包中的公共API
package com.example.myapp.api;
public class ServiceAPI {
public String getServiceInfo() {
return "Service Information";
}
}
// com.example.myapp.internal包中的內部實現
package com.example.myapp.internal;
import java.util.logging.Logger;
class ServiceImplementation {
private static final Logger LOGGER = Logger.getLogger(ServiceImplementation.class.getName());
void processData() {
LOGGER.info("Processing data...");
// 實現細節
}
}
使用 Maven 構建模塊化項目
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myapp</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>9</maven.compiler.source>
<maven.compiler.target>9</maven.compiler.target>
<maven.compiler.release>9</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>9</release>
</configuration>
</plugin>
</plugins>
</build>
</project>
使用 jlink 創建自定義運行時
jlink --module-path $JAVA_HOME/jmods:target/modules --add-modules com.example.myapp --launcher myapp=com.example.myapp/com.example.myapp.Main --output myapp-image
上述命令創建一個只包含應用程序所需模塊的自定義 JRE,大小可能只有標準 JRE 的一小部分。
模塊系統優勢分析
- 強封裝性:外部代碼無法訪問未明確導出的包,提高代碼安全性
- 明確依賴:模塊必須聲明其依賴關係,消除"classpath 地獄"
- 更小的運行時映像:使用
jlink可以創建僅包含所需模塊的自定義運行時 - 提高平台完整性:JDK 本身被模塊化,增強了安全性
- 更好的性能:啓動時間更短,內存佔用更少
注意事項與侷限性
- 遺留代碼兼容性:舊代碼需作為未命名模塊放在類路徑上,不能充分利用模塊系統優勢
- 遷移複雜性:大型項目遷移到模塊系統可能需要大量重構
- 依賴爆炸:如果不謹慎管理傳遞依賴,可能導致模塊圖變得複雜
- 反射限制:默認情況下,非導出包不允許反射訪問,需使用
opens顯式開放
遷移策略
對於現有項目,推薦採用以下遷移路徑:
- 作為自動模塊:先將 jar 放在模塊路徑而非類路徑,成為自動模塊
- 添加 module-info.java:逐步為每個組件添加模塊描述
- 拆分模塊:根據內聚性原則重構為多個模塊
- 管理依賴:重新考慮 exports 和 requires 語句,確保最小化暴露
2. JShell - Java 的交互式 REPL 環境
Java 9 引入了令人期待已久的 REPL(Read-Eval-Print Loop)工具,讓我們能快速測試 Java 代碼片段。
使用案例
啓動 JShell 很簡單:
$ jshell
| Welcome to JShell -- Version 9
| For an introduction type: /help intro
基本代碼測試:
jshell> int x = 10
x ==> 10
jshell> int y = 20
y ==> 20
jshell> x + y
$3 ==> 30
jshell> String greeting = "Hello, Java 9!"
greeting ==> "Hello, Java 9!"
jshell> greeting.toUpperCase()
$5 ==> "HELLO, JAVA 9!"
高級用法 - 導入外部類和定義類:
// 導入外部包
jshell> import java.util.concurrent.CompletableFuture
// 定義類
jshell> class Person {
...> private String name;
...> private int age;
...>
...> public Person(String name, int age) {
...> this.name = name;
...> this.age = age;
...> }
...>
...> public String toString() {
...> return name + ", " + age;
...> }
...> }
| 已創建 類 Person
// 使用剛定義的類
jshell> Person p = new Person("張三", 30)
p ==> 張三, 30
// 加載外部腳本
jshell> /open MyUtils.java
JShell 關鍵優勢
- 快速測試和學習:無需編寫完整類就能執行代碼
- 即時反饋:立即看到執行結果
- API 探索:快速嘗試和理解 API 功能
- 教學工具:特別適合學習和教學環境
- 原型設計:快速驗證算法或設計想法
侷限性
- 性能:不適合性能測試,因為 JShell 環境有額外開銷
- 持久化:會話結束後代碼丟失,雖可保存但不如正式項目管理
- 調試功能有限:複雜調試場景仍需 IDE
3. 私有接口方法
Java 8 引入了接口中的默認方法和靜態方法,Java 9 更進一步,允許在接口中使用私有方法。
使用案例
public interface PaymentProcessor {
// 公共抽象方法
void processPayment(double amount);
// 默認方法
default void processExpress(double amount) {
log("開始快速支付處理");
validateAmount(amount); // 調用私有方法
processPayment(amount);
log("快速支付處理完成");
}
default void processStandard(double amount) {
log("開始標準支付處理");
validateAmount(amount); // 複用相同邏輯
processPayment(amount);
log("標準支付處理完成");
}
// 私有方法 - Java 9新特性
private void validateAmount(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("支付金額必須大於零");
}
if (amount > 100000) {
log("大額支付警告:" + amount);
}
}
// 私有靜態方法 - Java 9新特性
private static void log(String message) {
System.out.println("支付日誌: " + message);
}
}
私有接口方法的優勢
- 代碼複用:允許在默認方法之間共享代碼,避免重複
- 更好的封裝:實現細節可以保持私有,不暴露給實現類
- 更乾淨的接口:避免了輔助方法的公開暴露
- 減少實現類負擔:無需在實現類中提供輔助功能
注意事項
- 只能在接口內部調用:私有方法不能被實現類或子接口訪問
- 靜態和非靜態區別:私有靜態方法可以被其他靜態和非靜態方法調用,但私有非靜態方法不能被靜態方法調用
4. 集合工廠方法
Java 9 之前,創建小型不可變集合很繁瑣。Java 9 引入了便捷的工廠方法。
使用案例對比
Java 8 方式:
// 創建不可變List
List<String> list = Collections.unmodifiableList(Arrays.asList("Java", "Kotlin", "Scala"));
// 創建不可變Set
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("紅", "綠", "藍")));
// 創建不可變Map
Map<String, Integer> map = Collections.unmodifiableMap(new HashMap<String, Integer>() {{
put("一", 1);
put("二", 2);
put("三", 3);
}});
Java 9 方式:
// 創建不可變List
List<String> list = List.of("Java", "Kotlin", "Scala");
// 創建不可變Set
Set<String> set = Set.of("紅", "綠", "藍");
// 創建不可變Map
Map<String, Integer> map = Map.of(
"一", 1,
"二", 2,
"三", 3
);
// 更多鍵值對使用ofEntries方法
Map<String, Integer> largeMap = Map.ofEntries(
Map.entry("一", 1), // Map.entry是靜態方法,創建Map.Entry實例
Map.entry("二", 2),
Map.entry("三", 3),
Map.entry("四", 4)
);
工廠方法的優勢
- 代碼簡潔:顯著減少了創建小型不可變集合的代碼量
- 不可變保證:返回的集合不允許修改,嘗試修改會拋出 UnsupportedOperationException
- 空值限制:不允許 null 元素,嘗試添加 null 會拋出 NullPointerException
- 性能優化:專為小型集合優化,內存佔用更少
侷限性
- 不支持 null 元素:如果嘗試創建包含 null 的集合(如 Set.of(null)),會立即拋出 NullPointerException
- 元素數量限制:直接的 of 方法最多支持 10 個元素(Map.of 最多支持 5 個鍵值對)
- 不可修改:如果需要後續修改集合,需使用其他構造方法
- 元素唯一性:Set.of 和 Map.of 會檢查重複,重複元素會導致 IllegalArgumentException
5. Stream API 增強
Java 9 為 Stream API 添加了幾個實用的新方法。
新方法使用案例
1. takeWhile() 和 dropWhile() - 短路操作
List<Integer> numbers = List.of(2, 4, 6, 8, 9, 10, 12);
// takeWhile:獲取元素,直到條件不滿足為止(短路操作)
List<Integer> evensUntilOdd = numbers.stream()
.takeWhile(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evensUntilOdd); // 輸出: [2, 4, 6, 8]
// dropWhile:丟棄元素,直到條件不滿足為止(短路操作)
List<Integer> afterFirstOdd = numbers.stream()
.dropWhile(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(afterFirstOdd); // 輸出: [9, 10, 12]
// 對比filter(非短路,處理整個流)
List<Integer> allEvens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(allEvens); // 輸出: [2, 4, 6, 8, 10, 12]
2. iterate 方法重載 - 有限流生成
// Java 8 的無限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
// 需要額外使用limit限制
infiniteStream.limit(10).forEach(System.out::println);
// Java 9 新增的有限流 - 添加了停止條件
Stream<Integer> evenNumbersUntil100 = Stream.iterate(0, n -> n <= 100, n -> n + 2);
evenNumbersUntil100.forEach(System.out::println); // 打印0到100的偶數,自動停止
3. ofNullable 方法 - 安全處理可能為 null 的值
// 模擬可能返回null的方法
public static String getNameFromDatabase(int id) {
// 假設id=1時返回名字,其他情況返回null
return id == 1 ? "張三" : null;
}
// 處理可能為null的值 - Java 8方式
String name = getNameFromDatabase(2);
Stream<String> nameStream = name == null ? Stream.empty() : Stream.of(name);
// Java 9方式 - 更簡潔
Stream<String> nameStream = Stream.ofNullable(getNameFromDatabase(2));
// 如果結果為null,返回空流而不是拋出異常
// 在處理可選數據時非常有用
List<String> names = List.of("user1", "user2", "user3");
List<String> validNames = names.stream()
.map(id -> getNameFromDatabase(Integer.parseInt(id.substring(4))))
.flatMap(Stream::ofNullable) // 過濾掉null值
.collect(Collectors.toList());
Stream 增強優勢分析
- 更精確的控制:takeWhile 和 dropWhile 提供了更細粒度的流控制,實現短路處理
- 簡化代碼:特別是處理有限序列和可能為 null 的值時,代碼更簡潔
- null 安全:ofNullable 方法避免了 NullPointerException,簡化空值處理
- 提高可讀性:使流處理邏輯更直觀,特別是有條件迭代時
- 性能優化:短路操作可以減少不必要的處理,提高效率
注意事項
- 順序敏感:takeWhile 和 dropWhile 對元素順序敏感,對無序流可能產生不可預測結果
- 有限流限制:iterate 的有限版本需要注意終止條件的設計,避免無限循環
- 語義區別:理解 takeWhile/dropWhile 與 filter 的本質區別(短路 vs 完整處理)
6. Optional 類增強
Optional 類在 Java 9 中獲得了三個新方法,使其更加強大和靈活。
新方法使用案例
1. ifPresentOrElse() - 同時處理存在和不存在情況
Optional<String> optional = Optional.of("Java 9");
// 同時處理存在和不存在的情況 - Java 8方式
if (optional.isPresent()) {
System.out.println("值存在: " + optional.get());
} else {
System.out.println("值不存在");
}
// Java 9方式 - 更簡潔
optional.ifPresentOrElse(
value -> System.out.println("值存在: " + value),
() -> System.out.println("值不存在")
);
2. or() - 提供備選 Optional
Optional<String> optional1 = Optional.empty();
Optional<String> optional2 = Optional.of("備選值");
// 如果optional1為空,則使用optional2 - Java 8方式
String result = optional1.isPresent() ? optional1.get() : optional2.orElse("默認值");
// Java 9方式 - 使用備選Optional
String result = optional1.or(() -> optional2).orElse("默認值");
System.out.println(result); // 輸出: 備選值
// 鏈式處理多個可選數據源
String value = getUserInput()
.or(() -> getFromCache())
.or(() -> getFromDatabase())
.or(() -> getDefaultValue())
.orElse("最終默認值");
3. stream() - 將 Optional 轉換為 Stream
Optional<String> optional = Optional.of("Java 9");
// 將Optional轉換為Stream
Stream<String> stream = optional.stream();
stream.forEach(System.out::println); // 輸出: Java 9
// 空Optional轉換為空Stream
Optional<String> emptyOptional = Optional.empty();
long count = emptyOptional.stream().count(); // 結果為0
// 在過濾操作中特別有用
List<Optional<String>> listOfOptionals = Arrays.asList(
Optional.of("Java"),
Optional.empty(),
Optional.of("Kotlin")
);
// Java 8方式 - 繁瑣
List<String> filteredList = listOfOptionals.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// Java 9方式 - 簡潔
List<String> filteredList = listOfOptionals.stream()
.flatMap(Optional::stream) // 只保留非空值
.collect(Collectors.toList());
System.out.println(filteredList); // 輸出: [Java, Kotlin]
Optional 增強優勢分析
- 更流暢的 API:新方法提供更連貫的編程體驗,減少條件判斷
- 更好的 Stream 集成:與 Stream API 的無縫集成,簡化集合處理
- 減少樣板代碼:特別是在處理多個 Optional 對象時,代碼更加簡潔
- 函數式風格:強化了函數式編程模式,使代碼更加聲明式
- 更優雅的空值處理:提供多種處理缺失值的策略,代碼更加健壯
注意事項
- 惰性求值:or()方法的 Supplier 參數僅在需要時才會被調用,有助於提高性能
- 性能考慮:鏈式調用過多可能導致性能開銷,應適度使用
- 空 Optional 處理:理解 stream()方法對空 Optional 的處理(轉換為空流)
7. HTTP/2 客户端
Java 9 引入了新的 HTTP 客户端 API,支持 HTTP/2 協議,替代了老舊的 HttpURLConnection。
使用案例
同步請求
// 創建HTTP客户端
HttpClient client = HttpClient.newHttpClient();
// 構建GET請求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/octocat"))
.header("User-Agent", "Java 9 HttpClient Bot")
.GET() // GET是默認方法,可省略
.build();
// 同步發送請求
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 處理響應
System.out.println("狀態碼: " + response.statusCode());
System.out.println("響應體: " + response.body());
異步請求與錯誤處理
// 配置客户端連接池和超時
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.executor(Executors.newFixedThreadPool(5)) // 自定義線程池
.build();
// 異步請求
CompletableFuture<String> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(body -> {
System.out.println("收到響應,長度: " + body.length());
return body;
})
.exceptionally(ex -> {
System.err.println("請求失敗: " + ex.getMessage());
return "Error occurred";
});
// 處理完成或取消
try {
String result = future.get(5, TimeUnit.SECONDS); // 設置超時
System.out.println(result);
} catch (TimeoutException e) {
future.cancel(true); // 取消請求
System.err.println("請求超時,已取消");
} finally {
// 關閉自定義線程池
ExecutorService executor = (ExecutorService)client.executor().orElse(null);
if (executor != null) {
executor.shutdown();
}
}
POST 請求示例
// 構建POST請求
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://postman-echo.com/post"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"張三\",\"age\":30}"))
.build();
// 發送請求
HttpResponse<String> response = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
新 HTTP 客户端的優勢
- 支持 HTTP/2:更快的響應、頭部壓縮、服務器推送等特性
- 同步和異步 API:靈活適應不同場景,簡化異步編程
- 流式請求和響應體:高效處理大型數據
- WebSocket 支持:內置的 WebSocket 實現
- 易用:比 HttpURLConnection 更直觀的 API
- 請求/響應鏈接:響應對象包含對應的請求對象
侷限性和注意事項
- 孵化器模塊:在 Java 9 中為孵化器模塊(即實驗性質的 API,可能在後續版本中變更),需要特殊引入,Java 11 才正式成為標準模塊
- 性能調優:對於高併發場景,需要適當配置連接池和線程策略
- 資源管理:大量異步請求需要注意資源管理和關閉
- 遷移成本:從 HttpURLConnection 遷移需要調整代碼邏輯
8. 增強的 Try-With-Resources
Java 9 改進了 try-with-resources 語句,使其更加簡潔。
使用案例對比
Java 7/8 方式:
// 資源需要在try-with-resources語句中聲明
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// 如果資源在外部聲明,需要重新聲明
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
try (BufferedReader r = reader) { // 重複聲明
String line = r.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
Java 9 方式:
// 外部聲明的資源可以直接在try中使用
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
try (reader) { // 使用已聲明的變量
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// 多個資源也可以簡化
Connection conn = DriverManager.getConnection(url, user, password);
Statement stmt = conn.createStatement();
try (conn; stmt) { // 同時管理兩個已聲明的資源
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
改進的優勢
- 更簡潔的代碼:避免了資源變量的重複聲明
- 提高可讀性:特別是當多個資源需要關閉時
- 減少錯誤:減少了複製粘貼錯誤的可能性
- 靈活性:允許在聲明和使用資源之間插入其他邏輯
限制條件
- final 或 effectively final:使用的變量必須是 final 或 effectively final
- AutoCloseable 要求:資源仍然必須實現 AutoCloseable 接口
- 變量作用域:使用的變量必須在 try 塊結束後仍在作用域內
9. 改進的鑽石操作符
Java 9 允許在匿名內部類中使用鑽石操作符,這在 Java 8 中是不允許的。
使用案例對比
Java 8 方式:
// 必須明確指定泛型類型
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
// 在構造泛型實例的匿名子類時也必須重複類型參數
List<Integer> numbers = new ArrayList<Integer>() {
@Override
public boolean add(Integer e) {
System.out.println("添加元素: " + e);
return super.add(e);
}
};
Java 9 方式:
// 可以使用鑽石操作符
Comparator<String> comparator = new Comparator<>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
};
// 匿名子類也可以使用鑽石操作符
List<Integer> numbers = new ArrayList<>() {
@Override
public boolean add(Integer e) {
System.out.println("添加元素: " + e);
return super.add(e);
}
};
優勢分析
- 代碼簡潔:減少類型重複聲明,提高可讀性
- 保持一致性:與局部變量類型推斷的風格一致
- 減少錯誤:避免類型不匹配的錯誤,特別是在複雜泛型類型中
- 維護性:修改類型時只需修改一處,降低維護成本
注意事項
- 類型推斷限制:某些複雜場景下編譯器可能無法正確推斷類型
- 向後兼容性:Java 9 代碼在 Java 8 環境中可能無法編譯
10. 進程 API 增強
Java 9 改進了對操作系統進程的控制和管理。
使用案例
// 獲取當前進程
ProcessHandle current = ProcessHandle.current();
System.out.println("當前進程ID: " + current.pid());
// 獲取進程信息
ProcessHandle.Info info = current.info();
System.out.println("命令: " + info.command().orElse("未知"));
System.out.println("啓動時間: " + info.startInstant().orElse(null));
System.out.println("用户: " + info.user().orElse("未知"));
System.out.println("CPU使用時間: " + info.totalCpuDuration().orElse(null));
// 列出所有進程
ProcessHandle.allProcesses()
.filter(ph -> ph.info().command().isPresent())
.forEach(ph -> System.out.println(ph.pid() + ": " + ph.info().command().get()));
// 獲取進程樹
current.children().forEach(child ->
System.out.println("子進程: " + child.pid()));
current.descendants().forEach(desc ->
System.out.println("所有後代進程: " + desc.pid()));
// 啓動新進程並等待終止
ProcessBuilder pb = new ProcessBuilder("notepad.exe");
Process process = pb.start();
ProcessHandle processHandle = process.toHandle();
// 異步監聽進程終止
processHandle.onExit().thenAccept(ph ->
System.out.println("進程 " + ph.pid() + " 已終止,退出值: " +
process.exitValue()));
// 強制終止進程
boolean terminated = processHandle.destroy();
// 或強制終止
boolean forciblyTerminated = processHandle.destroyForcibly();
進程 API 增強優勢
- 原生跨平台:無需使用平台特定代碼,統一 API 適用所有平台
- 進程信息:更豐富的進程元數據,包括啓動時間、命令行等
- 進程控制:更好的進程生命週期管理,包括優雅終止
- 異步通知:進程終止事件的異步處理,避免阻塞
- 進程樹:輕鬆訪問子進程、後代進程和父進程
侷限性
- 權限限制:某些操作系統限制可能導致部分信息不可用
- 平台差異:雖然 API 統一,但某些功能在不同平台上行為可能不同
- 性能開銷:頻繁查詢進程信息可能有性能開銷
11. 多分辨率圖像 API
Java 9 引入了處理多分辨率圖像的 API,特別適合高 DPI 顯示器。
使用案例
// 創建多分辨率圖像
List<Image> images = new ArrayList<>();
images.add(ImageIO.read(new File("icon-16.png"))); // 16x16
images.add(ImageIO.read(new File("icon-32.png"))); // 32x32
images.add(ImageIO.read(new File("icon-64.png"))); // 64x64
// 創建多分辨率圖像
MultiResolutionImage mrImage = new BaseMultiResolutionImage(images.toArray(new Image[0]));
// 獲取特定分辨率的變體
Image variant = mrImage.getResolutionVariant(32, 32); // 獲取最適合32x32顯示的圖像
// 獲取所有變體
List<Image> variants = mrImage.getResolutionVariants();
// 假設有圖形上下文
Graphics2D g2d = canvas.createGraphics();
// 在圖形上下文中使用多分辨率圖像 - 系統會自動選擇最佳分辨率
g2d.drawImage(mrImage, x, y, null);
// 根據顯示設備選擇合適的變體
double deviceScale = 2.0; // 模擬Retina顯示器
Image bestVariant = mrImage.getResolutionVariant(
16 * deviceScale, 16 * deviceScale); // 系統會選擇32x32的變體
多分辨率圖像 API 優勢
- 高 DPI 適配:在不同 DPI 屏幕上優化顯示效果,特別是 HiDPI 顯示器
- 自動選擇:系統根據顯示需求選擇最佳分辨率變體
- 兼容性:與現有圖像 API 無縫集成
- 簡化開發:減輕開發者處理多分辨率屏幕的負擔
- 平台一致性:跨平台提供一致的高質量顯示
限制和注意事項
- 內存消耗:存儲多個分辨率變體需要更多內存
- 性能考慮:處理大量多分辨率圖像可能影響性能
- 變體匹配算法:瞭解系統如何選擇最佳變體,確保提供合適的分辨率集
12. 其他值得關注的改進
1. Reactive Streams API
Java 9 引入了java.util.concurrent.Flow類,提供了響應式編程的標準 API。
// 發佈者
class MyPublisher implements Flow.Publisher<String> {
private List<Flow.Subscriber<? super String>> subscribers = new ArrayList<>();
@Override
public void subscribe(Flow.Subscriber<? super String> subscriber) {
subscribers.add(subscriber);
subscriber.onSubscribe(new Flow.Subscription() {
@Override
public void request(long n) {
// 發送數據
}
@Override
public void cancel() {
subscribers.remove(subscriber);
}
});
}
public void publish(String item) {
subscribers.forEach(s -> s.onNext(item));
}
}
// 訂閲者
class MySubscriber implements Flow.Subscriber<String> {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1); // 請求一個元素
}
@Override
public void onNext(String item) {
System.out.println("接收: " + item);
subscription.request(1); // 請求下一個元素
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("完成");
}
}
與 Spring Reactor 結合
// 使用Reactor庫實現響應式流
import reactor.core.publisher.Flux;
// 創建Flux發佈者
Flux<String> messages = Flux.just("消息1", "消息2", "消息3")
.concatWith(Flux.error(new RuntimeException("模擬錯誤")))
.concatWith(Flux.just("消息4"))
.onErrorReturn("錯誤後的備用消息");
// 訂閲處理
messages.subscribe(
msg -> System.out.println("接收: " + msg),
error -> System.err.println("錯誤: " + error),
() -> System.out.println("完成")
);
2. CompletableFuture API 改進
Java 9 為 CompletableFuture 添加了超時、延遲和其他增強功能。
// 添加超時
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // 模擬長時間運行
return "結果";
} catch (InterruptedException e) {
return "interrupted";
}
});
// 設置超時 - 1秒後超時並返回默認值
String result = future.completeOnTimeout("超時默認值", 1, TimeUnit.SECONDS)
.join();
System.out.println(result); // 輸出: 超時默認值
// 延遲執行
CompletableFuture<String> delayed = CompletableFuture
.supplyAsync(() -> "延遲結果")
.orTimeout(5, TimeUnit.SECONDS) // 5秒超時,拋出異常
.completeOnTimeout("超時值", 3, TimeUnit.SECONDS); // 3秒後返回默認值
3. Stack-Walking API
新的 Stack-Walking API 提供了更高效的堆棧遍歷方式。
// 獲取調用棧並過濾
StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
// 查找特定類的調用者
Optional<Class<?>> callerClass = walker.walk(frames ->
frames.filter(f -> !f.getClassName().equals(MyClass.class.getName()))
.findFirst()
.map(StackWalker.StackFrame::getDeclaringClass));
// 打印整個調用棧
walker.forEach(frame -> System.out.println(frame.getClassName() + "." +
frame.getMethodName() + " (行: " + frame.getLineNumber() + ")"));
// 收集特定深度的調用棧
List<String> callStack = walker.walk(frames ->
frames.limit(5) // 只獲取前5個幀
.map(frame -> frame.getClassName() + "." + frame.getMethodName())
.collect(Collectors.toList()));
4. 增強的 Deprecation
@Deprecated 註解增加了 forRemoval 和 since 屬性。
// Java 8方式
@Deprecated
public void oldMethod() {
// 實現
}
// Java 9方式 - 更豐富的信息
@Deprecated(since = "9", forRemoval = true)
public void veryOldMethod() {
// 這個方法將在未來版本中刪除
}
@Deprecated(since = "9", forRemoval = false)
public void legacyMethod() {
// 這個方法不推薦使用,但暫時不會刪除
}
總結
以下是 Java 9 主要新特性的總結表格:
| 特性 | 核心優勢 | 應用場景 | 注意事項 |
|---|---|---|---|
| 模塊系統 | 強封裝性、明確依賴、運行時優化 | 大型應用、庫開發、微服務 | 遺留代碼兼容性問題、反射限制 |
| JShell | 快速測試、即時反饋、原型設計 | 學習、原型開發、API 探索 | 不適用於性能測試、有限的調試 |
| 私有接口方法 | 代碼複用、更好封裝 | 接口設計、API 開發 | 僅接口內部可見、靜態方法限制 |
| 集合工廠方法 | 代碼簡潔、不可變保證 | 配置數據、常量集合 | 不支持 null(拋出 NullPointerException)、元素數量限制 |
| Stream API 增強 | 短路操作、簡化代碼、null 安全 | 數據處理、函數式編程 | 順序敏感、語義差異 |
| Optional 增強 | 流暢 API、更好集成、函數式風格 | 空值處理、函數式編程 | 惰性求值、性能考慮 |
| HTTP/2 客户端 | HTTP/2 支持、異步 API、WebSocket | Web 服務集成、API 調用 | 孵化器模塊、資源管理 |
| 改進的 Try-With-Resources | 更簡潔代碼、提高可讀性 | 資源管理、IO 操作 | 變量必須為 final 或 effectively final |
| 改進的鑽石操作符 | 代碼簡潔、類型安全 | 泛型編程、集合操作 | 複雜場景推斷限制 |
| 進程 API 增強 | 原生跨平台、豐富進程信息 | 系統監控、進程管理 | 權限限制、平台差異 |
| 多分辨率圖像 API | 高 DPI 適配、自動選擇 | GUI 開發、桌面應用 | 內存消耗、變體選擇算法 |
Java 9 雖然沒有 Java 8 那樣的語法革命,但它通過模塊系統和 API 改進為 Java 生態系統帶來了基礎架構級別的升級。這些變革為 Java 平台的長期發展奠定了基礎,特別是在微服務、容器化和雲原生應用開發方面。
模塊系統解決了"JAR 地獄"問題,提高了平台安全性,也為 JDK 本身的演進提供了更靈活的路徑。這為後續 Java 11、Java 17 等 LTS 版本中的重大改進鋪平了道路。模塊系統的引入使得 Java 平台能夠實現更精細的功能拆分,在 Java 11 中實現了模塊化 JRE,大幅減小了運行時的體積,為容器化部署和微服務架構提供了更好的支持。
遷移建議
對於想要利用 Java 9 模塊系統的企業開發者,建議採用以下漸進式遷移策略:
- 從自動模塊開始:首先將現有 JAR 放在模塊路徑上,讓它們成為自動模塊,不需要 module-info.java
- 增量添加模塊描述:為每個組件逐步添加模塊描述文件,優先從底層庫開始
- 重構內部依賴:基於"高內聚、低耦合"原則重新設計模塊邊界
- 利用 JPMS 工具:使用 jdeps 等工具分析依賴關係,幫助確定模塊結構
- 應用封裝特性:逐步應用強封裝,利用 exports 和 opens 限制不必要的 API 暴露
這種漸進式遷移可以最小化風險,同時逐步獲得模塊系統的好處。
感謝您耐心閲讀到這裏!如果覺得本文對您有幫助,歡迎點贊 👍、收藏 ⭐、分享給需要的朋友,您的支持是我持續輸出技術乾貨的最大動力!
如果想獲取更多 Java 技術深度解析,歡迎點擊頭像關注我,後續會每日更新高質量技術文章,陪您一起進階成長~