Jdk14 都要出了,Jdk9 的新特性還不了解一下?


點贊再看,動力無限。Hello world : ) 微信搜「 程序猿阿朗 」。

本文 Github.com/niumoo/JavaNotes未讀代碼博客 已經收錄,有很多知識點和系列文章。

Java 9

Java 9 中最大的亮點是 Java 平台模塊化的引入,以及模塊化 JDK。但是 Java 9 還有很多其他新功能,這篇文字會將重點介紹開發人員特別感興趣的幾種功能。

這篇文章也是 Java 新特性系列文章中的一篇,往期文章可以查看下面鏈接。

還看不懂同事的代碼?超強的 Stream 流操作姿勢還不學習一下

還看不懂同事的代碼?Lambda 表達式、函數接口了解一下

Jdk14 都要出了,還不能使用 Optional優雅的處理空指針?

Jdk14 都要出了,Jdk8 的時間處理姿勢還不了解一下?

還看不懂同事代碼?快來補一波 Java 7 語法特性

1. 模塊化

Java 9 中的模塊化是對 Java 的一次重大改進。但是模塊化並不是最近才提出來的,我們經常使用的 maven 構建工具,就是典型的模塊化構建工具。模塊化不僅讓模塊命名清晰,寫出高內聚低耦合的代碼,更可以方便處理模塊之間的調用關系。

Java 9 模塊系統

在 Oracle 官方中為 Java 9 中的模塊系統的定義如下:

the module, which is a named, self-describing collection of code and data. This module system.

直白翻譯:模塊是一個命名的,自我描述的代碼和數據的集合。

Java 9 不僅支持了模塊化開發,更是直接把 JDK 自身進行了模塊化處理。JDK 自身的模塊化可以帶來很多好處,比如:

  • 方便管理,越來越大的 JDK 在模塊化下結構變得更加清晰。
  • 模塊化 JDK 和 JRE 運行時鏡像可以提高性能、安全性、維護性。
  • 可以定制 JRE,使用更小的運行時鏡像,比如網絡應用不需要 swing 庫,可以在打包時選擇不用,減少性能消耗。
  • 清晰明了的模塊調用關系,避免調用不當出來的各種問題。

上面提到了 JDK 自身的模塊化,我們通過瀏覽 JDK 9 的目錄結構也可以發現一些變化。

JDK 模塊化

最明顯的是在 JDK 9 中 jre 文件夾不存在了。下面是在 IDEA 中查看的 JDK 9 的依賴,命名規范的模塊看起來是不是讓人賞心悅目呢?

JDK 9 在 IDEA

當然,這篇文章主要介紹 Java 9 的新特性,而模塊化是一個巨大改變,結合示例介紹下來篇幅會比較長,這里就不占用太多篇幅了。

模塊化文章預告:如何編寫一個模塊化系統,如何打包讓沒有安裝 Java 環境的系統運行編寫的代碼,都可以通過模塊化選擇運行時模塊實現。我后面的文章就會通過一個模塊化項目介紹到,有興趣的可以關注我后續文章 😎。

2. 集合工廠方法

在 Java 9 中為集合的創建增加了靜態工廠創建方式,也就是 of 方法,通過靜態工廠 of 方法創建的集合是只讀集合,里面的對象不可改變。並在不能存在 null,對於 setmap 集合,也不能存在 key 值重復。這樣不僅線程安全,而且消耗的內存也更小

下面是三種集合通過靜態工廠創建的方式。

// 工廠方法創建集合
List<String> stringList = List.of("a", "b", "c", "d");
Set<String> stringSet = Set.of("a", "b", "c", "d");
Map<String, Integer> stringIntegerMap = Map.of("key1", 1, "key2", 2, "key3", 3);
Map<String, Integer> stringIntegerMap2 = Map.ofEntries(Map.entry("key1", 1), Map.entry("key2", 2));

// 集合輸出
System.out.println(stringList);
System.out.println(stringSet);
System.out.println(stringIntegerMap);
System.out.println(stringIntegerMap2);

得到輸出結果。

[a, b, c, d]
[d, a, c, b]
{key2=2, key1=1, key3=3}
{key2=2, key1=1}

再次運行,得到輸出結果。

[a, b, c, d]
[a, c, b, d]
{key3=3, key2=2, key1=1}
{key2=2, key1=1}

為什么我貼了兩次運行結果呢?主要是要展示通過 of 方法創建的 setmap 集合在遍歷時,在每個 JVM 周期遍歷順序是隨機的,這樣的機制可以發下代碼中有沒有對於順序敏感的異常代碼。

這種只讀集合在 Java 9 之前創建是通過 Collections.unmodifiableList 修改集合操作權限實現的。

List<String> arrayList = new ArrayList<>();
arrayList.add("達西");
arrayList.add("未讀代碼");
// 設置為只讀集合
arrayList = Collections.unmodifiableList(arrayList);

靜態工廠 of 方法創建的集合還有一個特性,就是工廠內部會自由復用已有實例或者創建新的實例,所以應該避免對 of 創建的集合進行判等或者 haseCode 比較等操作。

像下面這樣,創建兩個 List,你會發現兩個 ListhashCode 是一樣的。

// 工廠可以自由創建新的實例或者復用現有實例,所以 使用 of 創建的集合,避免 == 或者 hashCode 判斷操作
List<String> stringList = List.of("a", "b", "c", "d");
List<String> stringList2 = List.of("a", "b", "c", "d");
System.out.println(stringList.hashCode());
System.out.println(stringList2.hashCode());
// 輸出結果
// 3910595
// 3910596

這也是使用 of 方法創建集合的優勢之一,消耗更少的系統資源。這一點也體現在 of 創建的集合的數據結構實現上,有興趣的同學可以自行研究下。

3. Stream API

Stream 流操作自從 Java 8 引入以來,一直廣受好評。便捷豐富的 Stream 操作讓人愛不釋手,更讓沒看過的同事眼花繚亂,在介紹 Java 8 新特性時已經對 Stream 進行了詳細的介紹,沒看過的同學可以看下這篇:

還看不懂同事的代碼?超強的 Stream 流操作姿勢還不學習一下

當然,學習 Stream 之前要先學習 Lambda ,如果你還沒有看過,也可以看下之前這篇:

還看不懂同事的代碼?Lambda 表達式、函數接口了解一下

Java 9 中,又對 Stream 進行了增強,主要增加了 4 個新的操作方法:dropWhile,takeWhile,ofNullable,iterate

下面對這幾個方法分別做個介紹。

  1. takeWhile: 從頭開始篩選,遇到不滿足的就結束了。

    // takeWhile ,從頭開始篩選,遇到不滿足的就結束了
    List<Integer> list1 = List.of(1, 2, 3, 4, 5);
    List<Integer> listResult = list1.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult);
    // takeWhile ,從頭開始篩選,遇到不滿足的就結束
    List<Integer> list2 = List.of(1, 2, 3, 4, 3, 0);
    List<Integer> listResult2 = list2.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult2);
    

    輸出結果。

    [1, 2]
    [1, 2]
    
  2. dropWhile: 從頭開始刪除,遇到不滿足的就結束了。

    // dropWhile ,從頭開始刪除,遇到不滿足的就結束了
    List<Integer> list1 = List.of(1, 2, 3, 4, 5);
    List<Integer> listResult = list1.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult);
    // dropWhile ,從頭開始刪除,遇到不滿足的就結束
    List<Integer> list2 = List.of(1, 2, 3, 4, 3, 0);
    List<Integer> listResult2 = list2.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult2);
    

    輸出結果。

    [3, 4, 5]
    [3, 4, 3, 0]
    
  3. ofNullable: 創建支持全 null 的 Stream.

    Stream<Integer> stream = Stream.of(1, 2, null);
    stream.forEach(System.out::print);
    System.out.println();
    // 空指針異常
    // stream = Stream.of(null);
    stream = Stream.ofNullable(null);
    stream.forEach(System.out::print);
    

    輸出結果。

    12null
    
  4. iterate: 可以重載迭代器。

    IntStream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::print);
    

    輸出結果。

    0123456789
    

Stream 增強之外,還增強了 OptionalOptional 增加了可以轉換成 Stream 的方法。

Stream<Integer> s = Optional.of(1).stream();
s.forEach(System.out::print);

4. 接口私有方法

Java 8 中增加了默認方法,在 Java 9 中又增加了私有方法,這時開始接口中不僅僅有了定義,還具有了行為。我想這是出於代碼構造上的考慮,如果沒有私有方法,那么當多個默認方法的行為一樣時,就要寫多個相同的代碼。而有了私有方法,事情就變得不一樣了。

就像下面的例子。

/**
 * @author 達西 - 公眾號:未讀代碼
 */
public class Jdk9Interface {
    public static void main(String[] args) {
        ChinaPeople chinaPeople = new ChinaPeople();
        chinaPeople.sleep();
        chinaPeople.eat();
        chinaPeople.doXxx();
    }

}

class ChinaPeople implements People {
    @Override
    public void sleep() {
        System.out.println("躺着睡");
    }
}

interface People {
    void sleep();

    default void eat() {
        drink();
    }

    default void doXxx() {
        drink();
    }

    private void drink() {
        System.out.println("喝水");
    }
}

例子中的接口 people 中的 eat()doXxx() 默認行為一致,使用私有方法可以方便的抽取一個方法出來。

輸出結果。

躺着睡
喝水
喝水

5. HTTP / 2 Client

Java 9 內置了新的 HTTP/2 客戶端,請求更加方便。

隨便訪問一個不存在的網頁。

HttpClient client = HttpClient.newHttpClient();
URI uri = URI.create("http://www.tianqiapi.com/api/xxx");
HttpRequest req = HttpRequest.newBuilder(uri).header("User-Agent", "Java").GET().build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());
String body = resp.body();
System.out.println(body);

輸出得到的結果,這里是這個網站的報錯信息。

There is no method xxxAction in ApiController

可能你運行的時候會報找不到 httpClient 模塊之類的問題,這時候需要你在你項目代碼目錄添加 httpClient 模塊 才能解決,添加方式看下面的圖。

Java 9 導入導出模塊

export 寫自己的包路徑,requires 寫引入的模塊名。

6. Java REPL - JShell

交互式的編程環境在其他語言如 Python 上早就有了,而 Java 上的交互式語言只到 Java 9才出現。交互式的編程可以讓開發者在輸入代碼的時候就獲取到程序的運行結果,而不用像之前一樣新建文件、創建類、導包、測試一系列流程。

JShell 中支持 tab 補全代碼以及自動添加分號,下面通過一個例子演示 JShell 的使用。

  1. 進入 JShell. 查看幫助文檔。

    C:\Users>jshell
    |  歡迎使用 JShell -- 版本 9
    |  要大致了解該版本, 請鍵入: /help intro
    jshell> /help
    |  鍵入 Java 語言表達式, 語句或聲明。
    |  或者鍵入以下命令之一:
    |  /list [<名稱或 id>|-all|-start]
    |       列出您鍵入的源
    |  /edit <名稱或 id>
    |       編輯按名稱或 id 引用的源條目
    |  /drop <名稱或 id>
    |       刪除按名稱或 id 引用的源條目
    |  /save [-all|-history|-start] <文件>
    |       將片段源保存到文件。
    |  /open <file>
    |       打開文件作為源輸入
    |  /vars [<名稱或 id>|-all|-start]
    |       列出已聲明變量及其值
    |  /methods [<名稱或 id>|-all|-start]
    |       列出已聲明方法及其簽名
    |  /types [<名稱或 id>|-all|-start]
    |       列出已聲明的類型
    |  /imports
    |       列出導入的項
    |  /exit
    |       退出 jshell
    |  /env [-class-path <路徑>] [-module-path <路徑>] [-add-modules <模塊>] ...
    |       查看或更改評估上下文
    |  /reset [-class-path <路徑>] [-module-path <路徑>] [-add-modules <模塊>]...
    |       重啟 jshell
    |  /reload [-restore] [-quiet] [-class-path <路徑>] [-module-path <路徑>]...
    |       重置和重放相關歷史記錄 -- 當前歷史記錄或上一個歷史記錄 (-restore)
    |  /history
    |       您鍵入的內容的歷史記錄
    |  /help [<command>|<subject>]
    |       獲取 jshell 的相關信息
    |  /set editor|start|feedback|mode|prompt|truncation|format ...
    |       設置 jshell 配置信息
    |  /? [<command>|<subject>]
    |       獲取 jshell 的相關信息
    |  /!
    |       重新運行上一個片段
    |  /<id>
    |       按 id 重新運行片段
    |  /-<n>
    |       重新運行前面的第 n 個片段
    |
    |  有關詳細信息, 請鍵入 '/help', 后跟
    |  命令或主題的名稱。
    |  例如 '/help /list' 或 '/help intro'。主題:
    |
    |  intro
    |       jshell 工具的簡介
    |  shortcuts
    |       片段和命令輸入提示, 信息訪問以及
    |       自動代碼生成的按鍵說明
    |  context
    |       /env /reload 和 /reset 的評估上下文選項
    
    jshell>
    
  2. 定義一個變量:a = 10,遍歷從 0 到 a 的數字。

    jshell> int a =10;
    a ==> 10
    jshell> for(int i=0;i<a;i++){System.out.println(i);}
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
  3. 定義一個集合,賦值1,2,3,4,5。然后輸出集合。

    jshell> List list = List.of(1,2,3,4,5);
    list ==> [1, 2, 3, 4, 5]
    jshell> list
    list ==> [1, 2, 3, 4, 5]
    
  4. 查看輸入過的代碼。

    jshell> /list
       1 : int a =10;
       2 : for(int i=0;i<a;i++){System.out.println(i);}
       3 : List list = List.of(1,2,3,4,5);
       4 : list
    
  5. 列出導入的包。

    jshell> /imports
    |    import java.io.*
    |    import java.math.*
    |    import java.net.*
    |    import java.nio.file.*
    |    import java.util.*
    |    import java.util.concurrent.*
    |    import java.util.function.*
    |    import java.util.prefs.*
    |    import java.util.regex.*
    |    import java.util.stream.*
    
  6. 將代碼保存到文件並退出。

    jshell> /save d:/JShell.java
    jshell> /exit
      再見
    

    在 D 盤看到的保存的代碼片段。

    JShell 保存的代碼

操作起來還是挺簡單的,還記得上面介紹集合工廠 of 方法創建出來的 setmap 數據在每個 JVM 周期里是無序的嘛?也可以用 JShell 實驗下。

Set.of 的隨機遍歷

7. 其他更新

Java 9 中增加或者優化的功能遠不止這些,上面只是列舉了常用的一些新特性,更多的新特性如:

  • 不能使用下划線 _ 作為變量名,因為它是一個關鍵字。
  • Javadoc 支持 HTML5 並且支持搜索功能。
  • Nashorn 引擎升級,更好的支持 Javascript.
  • String 存儲結構變更從 char -> byte.
  • .........

新特性很多,感興趣的可以自己了解下。

再次預告,后續文章會結合案例圖文並茂詳細介紹 Java 9 開始的模塊系統,感興趣的可以關注我。此去山高水遠,願你我一路同行。

文章案例都已經上傳到 Github:niumoo/jdk-feature

參考資料

<完>

Hello world : ) 我是阿朗,一線技術工具人,認認真真寫文章。

點贊的個個都是人才,不僅長得帥氣好看,說話還好聽。


文章持續更新,可以關注公眾號「 程序猿阿朗 」或訪問「未讀代碼博客 」。

回復【資料】有我准備的各系列知識點和必看書籍。

本文 Github.com/niumoo/JavaNotes 已經收錄,有很多知識點和系列文章,歡迎Star。

等你好久


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM