Java 10 新特性解析
文章目錄
- Java 10 新特性解析
- 1. 引言
- 2. 本地變量類型推斷(JEP 286)
- 2.1. 概述
- 2.2. 使用場景
- 2.3. 限制
- 2.4. 與之前版本的對比
- 2.5. 風格指南
- 2.6. 示例代碼
- 2.7. 優點與注意事項
- 3. 應用程序類數據共享(JEP 310)
- 3.1. 概述
- 3.2. 使用方法
- 3.3. 示例代碼
- 3.4. 性能優勢
- 3.5. 與之前版本的對比
- 3.6. 注意事項
- 4. 額外的Unicode語言標簽擴展(JEP 314)
- 4.1. 概述
- 4.2. 示例代碼
- 4.3. 與之前版本的對比
- 4.4. 注意事項
- 5. 根證書(JEP 319)
- 5.1. 概述
- 5.2. 影響
- 5.3. 與之前版本的對比
- 6. 基于時間的版本控制(JEP 322)
- 6.1. 概述
- 6.2. 示例
- 6.3. 影響
- 7. 其他增強功能
- 7.1. JEP 304: 垃圾收集器接口
- 7.2. JEP 307: G1的并行完全GC
- 7.3. JEP 312: 線程本地握手
- 7.4. JEP 313: 移除本地頭生成工具(javah)
- 7.5. JEP 316: 在替代內存設備上分配堆
- 7.6. JEP 317: 實驗性的基于Java的JIT編譯器
- 8. 其他API和選項
- 9. 結論
Java 10(JDK 10)于2018年3月發布,標志著Java進入了一個新的快速迭代時代,采用了六個月的發布周期。作為這一新節奏的首個版本,Java 10引入了多項新特性和增強功能,旨在提升開發者的生產力、優化應用程序性能并增強安全性。本文將深入探討Java 10的多個JDK增強提案(JEPs),重點介紹對開發者影響較大的特性,并提供完整的代碼示例和使用指南,確保讀者能夠充分理解并在實際項目中應用這些改進。
1. 引言
Java 10是Java SE平臺的一個重要里程碑,不僅因為它引入了新的語言特性和性能優化,還因為它標志著Java從傳統的三年發布周期轉向更快的六個月發布周期。這種變化使得新功能能夠更快地交付給開發者,但也意味著非長期支持(LTS)版本(如Java 10)的支持時間僅為六個月,開發者可能更傾向于在生產環境中使用LTS版本(如Java 11)。盡管如此,Java 10的特性為開發者提供了寶貴的實驗機會,并為后續版本奠定了基礎。
本文將詳細分析Java 10的多個JEP,重點關注對開發者日常工作有直接影響的特性,如本地變量類型推斷(JEP 286)、應用程序類數據共享(JEP 310)、額外的Unicode語言標簽擴展(JEP 314)、根證書(JEP 319)和基于時間的版本控制(JEP 322)。每個特性都將配有完整的代碼示例,并在適用的情況下提供與之前版本的對比,以展示優化的效果。我們還將簡要介紹其他內部改進,如垃圾收集器接口和線程本地握手等,以全面呈現Java 10的進步。
2. 本地變量類型推斷(JEP 286)
2.1. 概述
本地變量類型推斷是Java 10中最引人注目的特性,通過JEP 286引入。它允許開發者使用var
關鍵字聲明本地變量,而無需顯式指定類型,編譯器會根據初始化表達式的類型自動推斷變量類型。這一特性旨在減少Java代碼的冗余,提高可讀性和開發效率,同時保持Java的靜態類型安全。
在Java 10之前,聲明本地變量需要顯式指定類型,例如:
String message = "Hello, Java!";
List<String> list = new ArrayList<>();
Map<String, Integer> map = new HashMap<>();
這些聲明在類型顯而易見的情況下顯得冗長,尤其是在處理復雜泛型類型時。Java 10的var
關鍵字允許開發者簡化代碼:
var message = "Hello, Java!"; // 推斷為 String
var list = new ArrayList<String>(); // 推斷為 ArrayList<String>
var map = new HashMap<String, Integer>(); // 推斷為 HashMap<String, Integer>
2.2. 使用場景
var
關鍵字適用于以下場景:
- 本地變量:必須有初始化器,編譯器根據初始化器推斷類型。
- 增強型for循環:循環變量的類型可以推斷。
- 傳統for循環:循環計數器的類型可以推斷。
- try-with-resources:資源變量的類型可以推斷。
例如:
// 增強型 for 循環
var list = List.of("a", "b", "c");
for (var item : list) {System.out.println(item); // item 推斷為 String
}// 傳統 for 循環
for (var i = 0; i < 5; i++) {System.out.println(i); // i 推斷為 int
}// try-with-resources
try (var reader = new BufferedReader(new FileReader("file.txt"))) {String line = reader.readLine();System.out.println(line);
}
2.3. 限制
var
的使用有以下限制:
- 僅限于本地變量,不能用于方法參數、構造函數參數、方法返回值、字段或catch塊參數。
- 必須有初始化器,否則編譯器無法推斷類型。例如,
var x;
會導致編譯錯誤。 - 不支持數組初始化器,如
var arr = {1, 2, 3};
是無效的。 - 不支持lambda表達式參數(此功能在Java 11中引入)。
例如,以下代碼是無效的:
var x; // 錯誤:無法推斷類型
var arr = {1, 2, 3}; // 錯誤:不支持數組初始化器
public void method(var param) {} // 錯誤:不能用于方法參數
2.4. 與之前版本的對比
在Java 7中,引入了菱形操作符(<>
),允許在泛型實例化時省略類型參數,例如:
List<String> list = new ArrayList<>();
然而,左側仍需顯式聲明類型。Java 10的var
進一步減少了這種冗余,使代碼更簡潔。需要注意的是,var
推斷的是初始化器的具體類型,而不是接口類型。例如:
var list = new ArrayList<String>(); // 推斷為 ArrayList<String>,而不是 List<String>
如果需要接口類型,仍需顯式聲明:
List<String> list = new ArrayList<>();
2.5. 風格指南
為了確保var
的使用不影響代碼可讀性,OpenJDK提供了風格指南,以下是關鍵建議:
- G1:選擇有意義的變量名:變量名應反映其用途,例如
var customers = dbconn.executeQuery(query);
優于var custList = dbconn.executeQuery(query);
。 - G2:最小化變量作用域:較小的作用域降低類型變化導致錯誤的風險。
- G3:當初始化器提供足夠類型信息時使用
var
:例如,var outputStream = new ByteArrayOutputStream();
。 - G4:分解復雜表達式:使用
var
將長方法鏈拆分為更易讀的本地變量。 - G5:不必過分強調“面向接口編程”:對于本地變量,推斷具體類型(如
ArrayList
)的影響較小。 - G6:注意
var
與菱形操作符的結合:避免var list = new ArrayList<>();
,因為它推斷為ArrayList<Object>
。 - G7:謹慎使用數字字面量:
var x = 100;
推斷為int
,若需要long
,需寫var x = 100L;
。
2.6. 示例代碼
以下是一個綜合示例,展示var
在不同場景中的使用:
import java.util.*;
import java.io.*;public class VarExample {public static void main(String[] args) throws IOException {// 基本類型var message = "Hello, Java 10!";System.out.println(message); // 輸出:Hello, Java 10!// 集合var numbers = new ArrayList<Integer>();numbers.add(1);System.out.println(numbers); // 輸出:[1]// 流操作var stream = numbers.stream();var sum = stream.mapToInt(Integer::intValue).sum();System.out.println(sum); // 輸出:1// try-with-resourcestry (var reader = new BufferedReader(new FileReader("example.txt"))) {var line = reader.readLine();System.out.println(line);}// 匿名類var runnable = new Runnable() {public void run() {System.out.println("Running!");}};runnable.run();}
}
2.7. 優點與注意事項
優點:
- 減少樣板代碼,提高可讀性。
- 與其他語言(如C#、Scala)的類型推斷功能對齊。
- 無運行時開銷,類型在編譯時確定。
注意事項:
- 過度使用可能降低代碼可讀性,尤其當初始化器類型不明顯時。
- 不支持動態類型,Java仍為靜態類型語言。
- 需遵循風格指南,避免常見陷阱,如與菱形操作符結合導致推斷為
Object
。
有關更多詳細信息,請參閱JEP 286: Local-Variable Type Inference。
3. 應用程序類數據共享(JEP 310)
3.1. 概述
類數據共享(CDS)是Java 5引入的一項功能,允許多個JVM實例共享系統類的元數據,以減少內存占用和加快啟動時間。Java 10通過JEP 310擴展了CDS,允許將應用程序類包含在共享存檔中,進一步優化性能,特別是在運行多個JVM實例的場景中,如微服務或容器化環境。
3.2. 使用方法
要使用應用程序CDS,需執行以下步驟:
- 生成共享存檔:運行應用程序時,使用
-XX:ArchiveClassesAtExit
選項記錄加載的類。 - 使用共享存檔:啟動應用程序時,使用
-XX:SharedArchiveFile
選項加載存檔。
示例命令:
# 生成存檔
java -XX:ArchiveClassesAtExit=app.jsa -cp app.jar com.example.App# 使用存檔
java -XX:SharedArchiveFile=app.jsa -cp app.jar com.example.App
3.3. 示例代碼
以下是一個簡單的應用程序示例:
public class App {public static void main(String[] args) {System.out.println("Hello, World!");}
}
運行以下命令生成存檔:
java -XX:ArchiveClassesAtExit=app.jsa -cp app.jar App
然后使用存檔啟動應用程序:
java -XX:SharedArchiveFile=app.jsa -cp app.jar App
3.4. 性能優勢
根據JEP 310的成功指標:
- 在Java EE應用服務器上,6個JVM實例可節省約340MB內存。
- JEdit基準測試的啟動時間可提高20-30%。
- 嵌入式Felix基準測試顯示4個JVM進程的內存使用量減少18%。
這些優勢取決于加載的類和堆使用情況,但在多實例場景中尤為顯著。
3.5. 與之前版本的對比
在Java 9及之前,CDS僅限于系統類(由引導類加載器加載)。Java 10擴展了CDS,支持應用程序類路徑中的類以及由內置平臺和系統類加載器加載的類。未來的版本(如JEP 350)進一步支持動態CDS存檔。
3.6. 注意事項
- 共享存檔的存儲格式未標準化,可能因JVM版本而異。
- Java 10不支持用戶定義模塊(
--module-path
)的類存檔,需等待后續版本。
有關更多詳細信息,請參閱JEP 310: Application Class-Data Sharing。
4. 額外的Unicode語言標簽擴展(JEP 314)
4.1. 概述
Java 10通過JEP 314增強了java.util.Locale
類,支持BCP 47語言標簽的額外Unicode擴展。這些擴展允許更精確地控制區域設置相關的行為,如貨幣、每周第一天、區域覆蓋和時區。Java 7已支持ca
(日歷)和nu
(數字)擴展,Java 10新增了以下擴展:
cu
:貨幣類型fw
:每周第一天rg
:區域覆蓋tz
:時區
4.2. 示例代碼
以下示例展示如何使用這些擴展:
import java.util.*;
import java.text.*;public class LocaleExample {public static void main(String[] args) {// 指定歐元貨幣Locale localeEUR = Locale.forLanguageTag("en-US-u-cu-eur");NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(localeEUR);System.out.println(currencyFormat.format(1234.56)); // 輸出:€1,234.56// 指定每周第一天為星期一Locale localeFW = Locale.forLanguageTag("en-US-u-fw-mon");Calendar cal = Calendar.getInstance(localeFW);System.out.println(cal.getFirstDayOfWeek() == Calendar.MONDAY); // 輸出:true}
}
4.3. 與之前版本的對比
Java 9支持的ca
和nu
擴展已為日歷和數字格式提供了基礎支持。Java 10通過新增cu
、fw
、rg
和tz
擴展,進一步增強了國際化的靈活性。例如,開發者現在可以輕松指定非默認貨幣或時區,而無需復雜的配置。
4.4. 注意事項
- 擴展鍵對大小寫不敏感,但
Locale
類會將鍵和值規范化為小寫。 - 擴展值必須符合BCP 47的語法要求,否則可能拋出異常。
- 開發者需驗證擴展是否被目標API(如
NumberFormat
或Calendar
)支持。
有關更多詳細信息,請參閱JEP 314: Additional Unicode Language-Tag Extensions。
5. 根證書(JEP 319)
5.1. 概述
Java 10通過JEP 319在JDK的cacerts
密鑰庫中引入了一組默認的根認證機構(CA)證書。在OpenJDK 9中,cacerts
密鑰庫為空,導致無法在不提供自定義信任存儲的情況下建立TLS連接。Java 10通過包含Oracle Java SE根CA計劃的證書解決了這一問題。
5.2. 影響
此特性簡化了安全連接的配置,確保應用程序可以直接與使用受信任CA頒發的證書的服務器建立HTTPS連接。例如:
import javax.net.ssl.*;
import java.net.URL;public class TLSTest {public static void main(String[] args) throws Exception {URL url = new URL("https://www.oracle.com");HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();conn.connect();System.out.println("Connected successfully!");}
}
在Java 10中,上述代碼無需額外配置即可運行,因為cacerts
包含了必要的根證書。
5.3. 與之前版本的對比
在OpenJDK 9中,開發者需要手動配置信任存儲,否則TLS連接會失敗。Java 10的默認根證書消除了這一障礙,提高了開發效率和安全性。
有關更多詳細信息,請參閱JEP 319: Root Certificates。
6. 基于時間的版本控制(JEP 322)
6.1. 概述
Java 10通過JEP 322引入了基于時間的版本控制方案,以適應新的六個月發布周期。版本字符串采用$FEATURE.$INTERIM.$UPDATE.$PATCH
格式:
$FEATURE
:每六個月遞增的功能發布號,如10、11。$INTERIM
:保留為0,用于未來的中間更新。$UPDATE
:安全更新或補丁的編號。$PATCH
:緊急修復的編號。
例如,Java 10的初始版本為10.0.0,更新版本為10.0.1、10.0.2等。下一個功能發布為Java 11(11.0.0)。
6.2. 示例
檢查Java版本:
public class VersionCheck {public static void main(String[] args) {System.out.println(System.getProperty("java.version")); // 輸出:10.0.0}
}
6.3. 影響
此版本控制方案使開發者更容易跟蹤發布周期和支持狀態。Java 10作為非LTS版本,建議用于開發和測試,而生產環境可能更適合LTS版本(如Java 11)。
有關更多詳細信息,請參閱JEP 322: Time-Based Release Versioning。
7. 其他增強功能
Java 10還包括以下內部改進,對開發者影響較小,但對JVM維護和性能優化至關重要:
7.1. JEP 304: 垃圾收集器接口
改進了垃圾收集器(GC)的源代碼隔離,使添加或移除GC實現更加容易。這主要惠及JVM開發者,但為未來的GC創新奠定了基礎。
7.2. JEP 307: G1的并行完全GC
G1垃圾收集器是Java 9的默認GC,但其完全GC(Full GC)為單線程,可能導致性能瓶頸。Java 10通過JEP 307并行化完全GC,使用與年輕代和混合收集相同的并行工作線程。例如,可通過-XX:ParallelGCThreads
選項控制線程數:
java -XX:ParallelGCThreads=4 -jar app.jar
與之前版本的對比:在Java 9中,G1的完全GC為單線程,可能導致較長的暫停時間。并行完全GC顯著提高了性能,尤其是在大堆場景中。
7.3. JEP 312: 線程本地握手
此特性允許在不執行全局VM安全點的情況下對單個線程執行回調,減少延遲。安全點是JVM暫停所有線程以執行GC等操作的點,全局安全點可能導致顯著的延遲。線程本地握手提高了JVM的響應性,尤其在多線程應用中。
7.4. JEP 313: 移除本地頭生成工具(javah)
自Java 8起,javac
支持-h
選項生成本地頭文件,javah
工具變得多余。Java 10通過JEP 313移除javah
,開發者應使用以下命令:
javac -h . NativeExample.java
示例代碼:
public class NativeExample {public native void nativeMethod();
}
運行javac -h . NativeExample.java
將生成NativeExample.h
文件。
7.5. JEP 316: 在替代內存設備上分配堆
允許HotSpot VM在替代內存設備(如NV-DIMM)上分配Java堆,適用于特殊硬件環境,對普通開發者影響較小。
7.6. JEP 317: 實驗性的基于Java的JIT編譯器
引入Graal編譯器作為Linux/x64平臺的實驗性JIT編譯器,可通過以下選項啟用:
java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler -jar app.jar
Graal為未來的JVM性能優化(如Project Metropolis)奠定了基礎,但不建議在生產環境中使用。
有關更多詳細信息,請參閱:
- JEP 304: Garbage Collector Interface
- JEP 307: Parallel Full GC for G1
- JEP 312: Thread-Local Handshakes
- JEP 313: Remove the Native-Header Generation Tool
- JEP 316: Heap Allocation on Alternative Memory Devices
- JEP 317: Experimental Java-Based JIT Compiler
8. 其他API和選項
Java 10還引入了一些新的API和選項,增強了開發者的工具集。以下是一些關鍵更新:
類別 | 特性/增強功能 | 詳細信息 |
---|---|---|
core-libs/java.util | Optional.orElseThrow() 方法 | 新增方法,作為get 方法的首選替代,明確拋出異常。 |
core-libs/java.util:collections | 創建不可修改集合的API | 新增List.copyOf 、Set.copyOf 、Map.copyOf 和Collectors.toUnmodifiableList 等方法。 |
core-svc/java.lang.management | 禁用JRE最后使用跟蹤的系統屬性 | 新增jdk.disableLastUsageTracking 屬性,可通過-Djdk.disableLastUsageTracking=true 設置。 |
security-libs/javax.net.ssl | TLS會話哈希和擴展主密鑰支持 | 支持RFC 7627,可通過jdk.tls.useExtendedMasterSecret=false 禁用。 |
這些API提供了更便捷的操作方式,例如:
import java.util.*;public class CollectionExample {public static void main(String[] args) {var list = List.of("a", "b", "c");var unmodifiableList = List.copyOf(list); // 創建不可修改副本System.out.println(unmodifiableList); // 輸出:[a, b, c]}
}
9. 結論
Java 10通過其多個JEP為開發者帶來了顯著的改進,從本地變量類型推斷的便利性到應用程序類數據共享的性能優化,再到根證書和Unicode擴展的安全性與國際化增強。這些特性不僅提高了開發效率,還為Java的未來發展奠定了基礎。盡管Java 10作為非LTS版本的支持時間較短,但其特性為開發者提供了寶貴的實驗機會,并為Java 11等LTS版本鋪平了道路。
通過本文的代碼示例和詳細解釋,您應該能夠自信地在項目中應用這些新特性。建議開發者參考官方文檔和JEP頁面以獲取更深入的技術細節,并根據項目需求選擇是否升級到Java 10或等待LTS版本。
引用:
- JEP 286: Local-Variable Type Inference
- JEP 310: Application Class-Data Sharing
- JEP 314: Additional Unicode Language-Tag Extensions
- JEP 319: Root Certificates
- JEP 322: Time-Based Release Versioning
- JDK 10 Release Notes
- Local Variable Type Inference Style Guidelines