Java 9 新特性概述

原文地址:https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-9/index.html

ava 9 正式發布于 2017 年 9 月 21 日 。作為 Java8 之后 3 年半才發布的新版本,Java 9 帶 來了很多重大的變化。其中最重要的改動是 Java 平臺模塊系統的引入。除此之外,還有一些新的特性。 本文對 Java9 中包含的新特性做了概括性的介紹,可以幫助你快速了解 Java 9。

Java 平臺 模塊系統

Java 平臺模塊系統,也就是 Project Jigsaw,把模塊化開發實踐引入到了 Java 平臺中。在引入了模塊系統之后,JDK 被重新組織成 94 個模塊。Java 應用可以通過新增的 jlink 工具,創建出只包含所依賴的 JDK 模塊的自定義運行時鏡像。這樣可以極大的減少 Java 運行時環境的大小。這對于目前流行的不可變基礎設施的實踐來說,鏡像的大小的減少可以節省很多存儲空間和帶寬資源 。

模塊化開發的實踐在軟件開發領域并不是一個新的概念。Java 開發社區已經使用這樣的模塊化實踐有相當長的一段時間。主流的構建工具,包括 Apache Maven 和 Gradle 都支持把一個大的項目劃分成若干個子項目。子項目之間通過不同的依賴關系組織在一起。每個子項目在構建之后都會產生對應的 JAR 文件。 在 Java9 中 ,已有的這些項目可以很容易的升級轉換為 Java 9 模塊 ,并保持原有的組織結構不變。

Java 9 模塊的重要特征是在其工件(artifact)的根目錄中包含了一個描述模塊的 module-info.class 文 件。 工件的格式可以是傳統的 JAR 文件或是 Java 9 新增的 JMOD 文件。這個文件由根目錄中的源代碼文件 module-info.java 編譯而來。該模塊聲明文件可以描述模塊的不同特征。模塊聲明文件中可以包含的內容如下:

  • 模塊導出的包:使用 exports 可以聲明模塊對其他模塊所導出的包。包中的 public 和 protected 類型,以及這些類型的 public 和 protected 成員可以被其他模塊所訪問。沒有聲明為導出的包相當于模塊中的私有成員,不能被其他模塊使用。
  • 模塊的依賴關系:使用 requires 可以聲明模塊對其他模塊的依賴關系。使用 requires transitive 可 以把一個模塊依賴聲明為傳遞的。傳遞的模塊依賴可以被依賴當前模塊的其他模塊所讀取。 如果一個模塊所導出的類型的型構中包含了來自它所依賴的模塊的類型,那么對該模塊的依賴應該聲明為傳遞的。
  • 服務的提供和使用:如果一個模塊中包含了可以被 ServiceLocator 發現的服務接口的實現 ,需要使用 provides with 語句來聲明具體的實現類 ;如果一個模塊需要使用服務接口,可以使用 uses 語句來聲明。

代碼清單 1 中給出了一個模塊聲明文件的示例。在該聲明文件中,模塊 c om.mycompany.sample 導出了 Java 包 com.mycompany.sample。該模塊依賴于模塊 c om.mycompany.sample 。該模塊也提供了服務接口 com.mycompany.common.DemoService 的實現類 c om.mycompany.sample.DemoServiceImpl 。

清單 1. 模塊聲明示例

|

1

2

3

4

5

6

|

module com.mycompany.sample {

exports com.mycompany.sample;

requires com.mycompany.common;

provides com.mycompany.common.DemoService with

com.mycompany.sample.DemoServiceImpl;

}

|

模塊系統中增加了模塊路徑的概念。模塊系統在解析模塊時,會從模塊路徑中進行查找。為了保持與之前 Java 版本的兼容性,CLASSPATH 依然被保留。所有的類型在運行時都屬于某個特定的模塊。對于從 CLASSPATH 中加載的類型,它們屬于加載它們的類加載器對應的未命名模塊。可以通過 Class 的 getModule()方法來獲取到表示其所在模塊的 Module 對象。

在 JVM 啟動時,會從應用的根模塊開始,根據依賴關系遞歸的進行解析,直到得到一個表示依賴關系的圖。如果解析過程中出現找不到模塊的情況,或是在模塊路徑的同一個地方找到了名稱相同的模塊,模塊解析過程會終止,JVM 也會退出。Java 也提供了相應的 API 與模塊系統進行交互。

Jshell

jshell 是 Java 9 新增的一個實用工具。jshell 為 Java 增加了類似 NodeJS 和 Python 中的讀取-求值-打印循環( Read-Evaluation-Print Loop ) 。 在 jshell 中 可以直接 輸入表達式并查看其執行結果。當需要測試一個方法的運行效果,或是快速的對表達式進行求值時,jshell 都非常實用。只需要通過 jshell 命令啟動 jshell,然后直接輸入表達式即可。每個表達式的結果會被自動保存下來 ,以數字編號作為引用,類似 $1 和$2 這樣的名稱 。可以在后續的表達式中引用之前語句的運行結果。 在 jshell 中 ,除了表達式之外,還可以創建 Java 類和方法。jshell 也有基本的代碼完成功能。

代碼清單 2 中,我們直接創建了一個方法 add。

清單 2. 在 jshell 中添加方法

|

1

2

3

4

|

jshell> int add(int x, int y) {

...> return x + y;

...> }

| created method add(int,int)

|

接著就可以在 jshell 中直接使用這個方法,如 代碼清單 3 所示。

清單 3. 在 jshell 中使用創建的方法

|

1

2

|

jshell> add(1, 2)

$19 ==> 3

|

集合、Stream 和 Optional

在集合上,Java 9 增加 了 List.of()、Set.of()、Map.of() 和 M ap.ofEntries()等工廠方法來創建不可變集合 ,如 代碼清單 4 所示。

清單 4 . 創建不可變集合

|

1

2

3

4

5

6

7

8

|

List.of();

List.of("Hello", "World");

List.of(1, 2, 3);

Set.of();

Set.of("Hello", "World");

Set.of(1, 2, 3);

Map.of();

Map.of("Hello", 1, "World", 2);

|

Stream 中增加了新的方法 ofNullable、dropWhile、takeWhile 和 iterate。在 代碼清單 5 中,流中包含了從 1 到 5 的 元素。斷言檢查元素是否為奇數。第一個元素 1 被刪除,結果流中包含 4 個元素。

清單 5 . Stream 中的 dropWhile 方法示例

|

1

2

3

4

5

6

7

|

@Test

public void testDropWhile() throws Exception {

final long count = Stream.of(1, 2, 3, 4, 5)

.dropWhile(i -> i % 2 != 0)

.count();

assertEquals(4, count);

}

|

Collectors 中增加了新的方法 filtering 和 flatMapping。在 代碼清單 6 中,對于輸入的 String 流 ,先通過 flatMapping 把 String 映射成 Integer 流 ,再把所有的 Integer 收集到一個集合中。

清單 6 . Collectors 的 flatMapping 方法示例

|

1

2

3

4

5

6

7

|

@Test

public void testFlatMapping() throws Exception {

final Set<``Integer``> result = Stream.of("a", "ab", "abc")

.collect(Collectors.flatMapping(v -> v.chars().boxed(),

Collectors.toSet()));

assertEquals(3, result.size());

}

|

Optiona l 類中新增了 ifPresentOrElse、or 和 stream 等方法。在 代碼清單 7 中,Optiona l 流中包含 3 個 元素,其中只有 2 個有值。在使用 flatMap 之后,結果流中包含了 2 個值。

清單 7 . Optional 的 stream 方法示例

|

1

2

3

4

5

6

7

8

9

10

|

@Test

public void testStream() throws Exception {

final long count = Stream.of(

Optional.of(1),

Optional.empty(),

Optional.of(2)

).flatMap(Optional::stream)

.count();

assertEquals(2, count);

}

|

進程 API

Java 9 增加了 ProcessHandle 接口,可以對原生進程進行管理,尤其適合于管理長時間運行的進程。在使用 P rocessBuilder 來啟動一個進程之后,可以通過 Process.toHandle()方法來得到一個 ProcessHandl e 對象的實例。通過 ProcessHandle 可以獲取到由 ProcessHandle.Info 表 示的進程的基本信息,如命令行參數、可執行文件路徑和啟動時間等。ProcessHandle 的 onExit()方法返回一個 C ompletableFuture<ProcessHandle>對象,可以在進程結束時執行自定義的動作。 代碼清單 8 中給出了進程 API 的使用示例。

清單 8 . 進程API 示例

|

1

2

3

4

5

6

7

8

9

10

|

final ProcessBuilder processBuilder = new ProcessBuilder("top")

.inheritIO();

final ProcessHandle processHandle = processBuilder.start().toHandle();

processHandle.onExit().whenCompleteAsync((handle, throwable) -> {

if (throwable == null) {

System.out.println(handle.pid());

} else {

throwable.printStackTrace();

}

});

|

平臺日志 API 和 服務

Java 9 允許為 JDK 和應用配置同樣的日志實現。新增的 System.LoggerFinder 用來管理 JDK 使 用的日志記錄器實現。JVM 在運行時只有一個系統范圍的 LoggerFinder 實例。LoggerFinder 通 過服務查找機制來加載日志記錄器實現。默認情況下,JDK 使用 java.logging 模塊中的 java.util.logging 實現。通過 LoggerFinder 的 getLogger()方法就可以獲取到表示日志記錄器的 System.Logger 實現。應用同樣可以使用 System.Logger 來記錄日志。這樣就保證了 JDK 和應用使用同樣的日志實現。我們也可以通過添加自己的 System.LoggerFinder 實現來讓 JDK 和應用使用 SLF4J 等其他日志記錄框架。 代碼清單 9 中給出了平臺日志 API 的使用示例。

清單 9.使用平臺日志 API

|

1

2

3

4

5

6

|

public class Main {

private static final System.Logger LOGGER = System.getLogger("Main");

public static void main(final String[] args) {

LOGGER.log(Level.INFO, "Run!");

}

}

|

反應式流 ( Reactive Streams )

反應式編程的思想最近得到了廣泛的流行。 在 Java 平臺上有流行的反應式 庫 RxJava 和 R eactor。反應式流規范的出發點是提供一個帶非阻塞負壓( non-blocking backpressure ) 的異步流處理規范。反應式流規范的核心接口已經添加到了 Java9 中的 java.util.concurrent.Flow 類中。

Flow 中包含了 Flow.Publisher、Flow.Subscriber、Flow.Subscription 和 F low.Processor 等 4 個核心接口。Java 9 還提供了 SubmissionPublisher 作為 Flow.Publisher 的一個實現。RxJava 2 和 Reactor 都可以很方便的 與 Flow 類的核心接口進行互操作。

變量句柄

變量句柄是一個變量或一組變量的引用,包括靜態域,非靜態域,數組元素和堆外數據結構中的組成部分等。變量句柄的含義類似于已有的方法句柄。變量句柄由 J ava 類 java.lang.invoke.VarHandle 來表示。可以使用類 j ava.lang.invoke.MethodHandles.Looku p 中的靜態工廠方法來創建 VarHandle 對 象。通過變量句柄,可以在變量上進行各種操作。這些操作稱為訪問模式。不同的訪問模式尤其在內存排序上的不同語義。目前一共有 31 種 訪問模式,而每種訪問模式都 在 VarHandle 中 有對應的方法。這些方法可以對變量進行讀取、寫入、原子更新、數值原子更新和比特位原子操作等。VarHandle 還 可以用來訪問數組中的單個元素,以及把 byte[]數組 和 ByteBuffer 當成是不同原始類型的數組來訪問。

代碼清單 10 中,我們創建了訪問 HandleTarget 類中的域 count 的變量句柄,并在其上進行讀取操作。

清單 10. 變量句柄使用示例

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

|

public class HandleTarget {

public int count = 1;

}

public class VarHandleTest {

private HandleTarget handleTarget = new HandleTarget();

private VarHandle varHandle;

@Before

public void setUp() throws Exception {

this.handleTarget = new HandleTarget();

this.varHandle = MethodHandles

.lookup()

.findVarHandle(HandleTarget.class, "count", int.class);

}

@Test

public void testGet() throws Exception {

assertEquals(1, this.varHandle.get(this.handleTarget));

assertEquals(1, this.varHandle.getVolatile(this.handleTarget));

assertEquals(1, this.varHandle.getOpaque(this.handleTarget));

assertEquals(1, this.varHandle.getAcquire(this.handleTarget));

}

}

|

改進方法句柄(Method Handle)

類 java.lang.invoke.MethodHandles 增加了更多的靜態方法來創建不同類型的方法句柄。

  • arrayConstructor:創建指定類型的數組。
  • arrayLength:獲取指定類型的數組的大小。
  • varHandleInvoker 和 varHandleExactInvoker:調用 VarHandle 中的訪問模式方法。
  • zero:返回一個類型的默認值。
  • empty:返 回 MethodType 的返回值類型的默認值。
  • loop、countedLoop、iteratedLoop、whileLoop 和 doWhileLoop:創建不同類型的循環,包括 for 循環、while 循環 和 do-while 循環。
  • tryFinally:把對方法句柄的調用封裝在 try-finally 語句中。

代碼清單 11 中,我們使用 iteratedLoop 來創建一個遍歷 S tring 類型迭代器的方法句柄,并計算所有字符串的長度的總和。

清單 11. 循環方法句柄使用示例

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

|

public class IteratedLoopTest {

static int body(final int sum, final String value) {

return sum + value.length();

}

@Test

public void testIteratedLoop() throws Throwable {

final MethodHandle iterator = MethodHandles.constant(

Iterator.class,

List.of("a", "bc", "def").iterator());

final MethodHandle init = MethodHandles.zero(int.class);

final MethodHandle body = MethodHandles

.lookup()

.findStatic(

IteratedLoopTest.class,

"body",

MethodType.methodType(

int.class,

int.class,

String.class));

final MethodHandle iteratedLoop = MethodHandles

.iteratedLoop(iterator, init, body);

assertEquals(6, iteratedLoop.invoke());

}

}

|

并發

在并發方面,類 CompletableFuture 中增加了幾個新的方法。completeAsync 使用一個異步任務來獲取結果并完成該 CompletableFuture。orTimeout 在 CompletableFuture 沒有在給定的超時時間之前完成,使用 TimeoutException 異常來完成 CompletableFuture。completeOnTimeout 與 o rTimeout 類似,只不過它在超時時使用給定的值來完成 CompletableFuture。新的 Thread.onSpinWai t 方法在當前線程需要使用忙循環來等待時,可以提高等待的效率。

Nashorn

Nashorn 是 Java 8 中引入的新的 JavaScript 引擎。Java 9 中的 Nashorn 已經實現了一些 ECMAScript 6 規范中的新特性,包括模板字符串、二進制和八進制字面量、迭代器 和 for..of 循環和箭頭函數等。Nashorn 還提供了 API 把 ECMAScript 源代碼解析成抽象語法樹( Abstract Syntax Tree,AST ) ,可以用來對 ECMAScript 源代碼進行分析。

I/O 流新特性

類 java.io.InputStream 中增加了新的方法來讀取和復制 InputStream 中包含的數據。

  • readAllBytes:讀取 InputStream 中的所有剩余字節。
  • readNBytes: 從 InputStream 中讀取指定數量的字節到數組中。
  • transferTo:讀取 InputStream 中的全部字節并寫入到指定的 OutputStream 中 。

代碼清單 12 中給出了這些新方法的使用示例。

清單 12. InputStream 中的新方法使用示例

|

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

|

public class TestInputStream {

private InputStream inputStream;

private static final String CONTENT = "Hello World";

@Before

public void setUp() throws Exception {

this.inputStream =

TestInputStream.class.getResourceAsStream("/input.txt");

}

@Test

public void testReadAllBytes() throws Exception {

final String content = new String(this.inputStream.readAllBytes());

assertEquals(CONTENT, content);

}

@Test

public void testReadNBytes() throws Exception {

final byte[] data = new byte[5];

this.inputStream.readNBytes(data, 0, 5);

assertEquals("Hello", new String(data));

}

@Test

public void testTransferTo() throws Exception {

final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

this.inputStream.transferTo(outputStream);

assertEquals(CONTENT, outputStream.toString());

}

}

|

ObjectInputFilter 可以對 ObjectInputStream 中 包含的內容進行檢查,來確保其中包含的數據是合法的。可以使用 ObjectInputStream 的方法 setObjectInputFilter 來設置。ObjectInputFilter 在 進行檢查時,可以檢查如對象圖的最大深度、對象引用的最大數量、輸入流中的最大字節數和數組的最大長度等限制,也可以對包含的類的名稱進行限制。

改進應用安全性能

Java 9 新增了 4 個 SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 S HA3-512。另外也增加了通過 java.security.SecureRandom 生成使用 DRBG 算法的強隨機數。 代碼清單 13 中給出了 SHA-3 哈希算法的使用示例。

清單 13. SHA-3 哈希算法使用示例

清單 13. SHA-3 哈希算法使用示例

|

1

2

3

4

5

6

7

8

|

import org.apache.commons.codec.binary.Hex;

public class SHA3 {

public static void main(final String[] args) throws NoSuchAlgorithmException {

final MessageDigest instance = MessageDigest.getInstance("SHA3-224");

final byte[] digest = instance.digest("".getBytes());

System.out.println(Hex.encodeHexString(digest));

}

}

|

用戶界面

類 java.awt.Desktop 增加了新的與桌面進行互動的能力。可以使用 addAppEventListener 方法來添加不同應用事件的監聽器,包括應用變為前臺應用、應用隱藏或顯示、屏幕和系統進入休眠與喚醒、以及 用戶會話的開始和終止等。還可以在顯示關于窗口和配置窗口時,添加自定義的邏輯。在用戶要求退出應用時,可以通過自定義處理器來接受或拒絕退出請求。在 A WT 圖像支持方面,可以在應用中使用多分辨率圖像。

統一 JVM 日志

Java 9 中 ,JVM 有了統一的日志記錄系統,可以使用新的命令行選項-Xlog 來控制 JVM 上 所有組件的日志記錄。該日志記錄系統可以設置輸出的日志消息的標簽、級別、修飾符和輸出目標等。Java 9 移除了在 Java 8 中 被廢棄的垃圾回收器配置組合,同時 把 G1 設為默認的垃圾回收器實現。另外,CMS 垃圾回收器已經被聲明為廢棄。Java 9 也增加了很多可以通過 jcmd 調用的診斷命令。

其他改動方面

在 Java 語言本身,Java 9 允許在接口中使用私有方法。 在 try-with-resources 語句中可以使用 e ffectively-final 變量。 類 java.lang.StackWalker 可 以對線程的堆棧進行遍歷,并且支持過濾和延遲訪問。Java 9 把對 Unicode 的支持升級到了 8.0。ResourceBundle 加載屬性文件的默認編碼從 ISO-8859-1 改成了 UTF-8,不再需要使用 native2ascii 命 令來對屬性文件進行額外處理。注解@Deprecated 也得到了增強,增加了 since 和 forRemoval 兩 個屬性,可以分別指定一個程序元素被廢棄的版本,以及是否會在今后的版本中被刪除。

代碼清單 14 中,buildMessage 是接口 SayHi 中的私有方法,在默認方法 sayHi 中被使用。

清單 14. 接口中私有方法的示例

|

1

2

3

4

5

6

7

8

9

|

public interface SayHi {

private String buildMessage() {

return "Hello";

}

void sayHi(final String message);

default void sayHi() {

sayHi(buildMessage());

}

}

|

小結

作為 Java 平臺最新的一個重大更新,Java 9 中的很多新特性,尤其模塊系統,對于 Java 應用的開發會產生深遠的影響。本文對 Java 9 中的新特性做了概括的介紹,可以作為了解 Java 9 的基礎。這些新特性的相信內容,可以通過官方文檔來進一步的了解。

參考資源 (resources )

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 結隊參觀四十里,西路軍魂應在此。 涼州戰役奈若何?浴血當年何懼死。 指揮若定欲向前,千古英名著青史。 力敵馬匪喊殺...
    雪窗_武立之閱讀 1,643評論 0 5
  • 看到第六集,我才對電視劇有了一些了解。 我突然意識到,三十五歲的男主,他,代表了日本的宅男啊,他不是自己一個人,他...
    真愛521閱讀 450評論 0 0
  • 緣起 2017年3月21-22日在廣州上完葉老師的時間管理線下課,當時因為是第一次接觸“易效能”,不像其他大多數同...
    夢想之旅_馨閱讀 227評論 0 1
  • 高考一結束,張軍和王梅就各自跟自己的配偶離婚了。據不完全統計,高考結束這幾天,也是中國家庭離婚率的高發期,父母為了...
    撿盡寒枝不肯棲閱讀 409評論 0 0