JDK9 新特性


想了解更詳細的JDK9新特性可以瀏覽官方介紹

JDK9 新特性目錄導航

  • 目錄結構
  • 模塊化系統
  • jshell
  • 多版本兼容JAR
  • 接口的私有方法
  • 改進try-with-resourcs
  • 改進磚石操作符
  • 限制使用單獨下划線標識符
  • String存儲結構變更
  • 快速創建只讀結合
  • 增強Stream API
  • 改進Optional 類
  • 多分辨率圖像 API
  • 全新 HTTP客服端API
  • 智能JAVA 編譯工具
  • 統一JVM 日志系統
  • javadoc 的 HTML5 支持
  • java 動態編譯

目錄結構

JDK9具體目錄結構如下所示:

  • bin: 該目錄包含所有的命令。
  • conf: 包含用戶可以編輯的配置文件,例如以前位於jre\lib 目錄中的.properties 和 .policy 文件。
  • include: 包含一些編譯本地代碼時使用的C/C++頭文件。
  • jmods: 包含JMOD 格式的平台模塊,創建自定義運行映射時需要它。
  • legal: 包含法律聲明。
  • lib: 包含非Windows 平台上動態鏈接的本地庫,其子目錄和文件不應由開發人員直接編輯或使用。

注:JDK9 目錄中不再有jre子目錄。

模塊化系統

JDK9將JDK分成一組模塊,可以在編譯時,運行時或構建時進行組合。模塊化可以減少內存開銷;只需必要的模塊,並非全部模塊,可以簡化各種類庫和大型應用的開發和維護。思考如下示例:

模塊目錄結構:

modela的module-info.java的內容

1 module modela {
2     exports com.cnblogs.bean;
3 }

modelb的module-info.java的內容

1 module modelb {
2     requires modela;
3 }

modelb的Test類

 1 package com.cnblogs.tset;
 2 
 3 import com.cnblogs.bean.Person;
 4 
 5 public class Test {
 6 
 7     public static void main(String[] args) {
 8         Person person = new Person("念念就忘",20);
 9         System.out.println(person);
10     }
11 
12 }

輸出結果:

Person{name='念念就忘', age=20}

如上示例,如果需要在modelb項目中使用modela項目的內容,必須在modela項目的module-info.java中定義exports com.cnblogs.bean將該目錄下可以被其他模塊使用,如果沒有寫,則包默認是封裝在模塊下,不被外界使用。而在modela項目中需要使用requires modela導入需要使用的模塊名,則可以在modelb中使用modela定義exports的類(即才可以在Test類中使用Person類)。

module-info.java:該文件必須位於項目的根目錄中。該文件用於定義模塊需要什么依賴,以及那些包被外部使用。

exports:控制着那些包可以被其他模塊訪問到,所有不被exports的包默認都被封裝在模塊里面不被外界所使用。

requires:指明對其他模塊的依賴。

jshell

JDK9新增了REPL(Read-Eval-Print Loop)工具jshell,jshell工具提供了一個交互式命令界面,可以評估聲明,語句和表達式,無需編譯即可返回執行結果。無論是在初學JAVA或學習新的API時都非常有用。

如上圖所示,只需要在cmd中輸入jshell命令,即可啟動jshell界面。同時提醒你使用help intro可以大致了解該版本

如上圖,jshell基本使用規則。每一條語句的末尾可以使用分好和不使用分好。

jshell也可以定義方法

退出jshell使用/exit

jshell詳細介紹,請參考官方介紹文檔 

多版本兼容JAR

當一個新版本的 Java 出現的時候,你的庫用戶要花費數年時間才會切換到這個新的版本。這就意味着庫得去向后兼容你想要支持的最老的 Java 版本(許多情況下就是 Java 6 或者 Java7)。這實際上意味着未來的很長一段時間,你都不能在庫中運用 Java 9 所提供的新特性。幸運的是,多版本兼容 jar 功能能讓你創建僅在特定版本的 Java 環境中運行庫程序選擇使用的 class 版本。

使用舉例如下:

在指定目錄【F:\peter\ideaWork\java9test\src\java\com\cnblogs\TestJar.java】新建TestJar.java文件

1 package com.cnblogs;
2 
3 public class TestJar {
4 
5     public String getValue(){
6         return "JAVA 8";
7     }
8 
9 }

在指定目錄【F:\peter\ideaWork\java9test\src\java\com\cnblogs\Test.java】新建Test.java文件

 1 package com.cnblogs;
 2 
 3 public class Test {
 4 
 5     public static void main(String[] args) {
 6         TestJar testJar = new TestJar();
 7         System.out.println(testJar.getValue());
 8     }
 9 
10 }

在指定目錄【F:\peter\ideaWork\java9test\src\java9\com\cnblogs\TestJar.java】新建TestJar.java文件,這個是向后兼容JDK9的文件

1 package com.cnblogs;
2 
3 public class TestJar {
4     public String getValue(){
5         return "JAVA 9";
6     }
7 }

執行如下命令進行編譯:

F:\peter\ideaWork\java9test>javac -d build --release 8 src/java/com/cnblogs/*.java

F:\peter\ideaWork\java9test>javac -d build9 --release 9 src/java9/com/cnblogs/TestJar.java

F:\peter\ideaWork\java9test>jar --create --main-class=Test --file multijar.jar -C build . --release 9 -C build9 .

在JDK9下運行如下命令,輸出結果為 JAVA 9

F:\peter\ideaWork\java9test>java -version
openjdk version "9"
OpenJDK Runtime Environment (build 9+181)
OpenJDK 64-Bit Server VM (build 9+181, mixed mode)

F:\peter\ideaWork\java9test> java -cp multijar.jar com.cnblogs.Test
JAVA 9

在JDK8下運行如下命令,輸出結果為JAVA 8

F:\peter\ideaWork\java9test>java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

F:\peter\ideaWork\java9test>java -cp multijar.jar com.cnblogs.Test
JAVA 8

接口的私有方法

在JDK8中接口運行使用靜態方法和默認方法后,JDK9可以在接口中使用私有方法。如下示例:

 1 public interface MyInterface {
 2 
 3     void normalInterfaceMethod();
 4 
 5     default void defaultMethod1(){
 6         privateMethod();
 7     }
 8 
 9     default void defaultMethod2(){
10         privateMethod();
11     }
12 
13     //接口的私有方法可以在JDK9中使用
14     private void privateMethod(){
15         System.out.println("這是一個接口的私有方法");
16     }
17 }
1 public class MyClass implements MyInterface{
2 
3     @Override
4     public void normalInterfaceMethod() {
5         System.out.println("實現接口的默認方法");
6     }
7 }
1 public class Test {
2     public static void main(String[] args) {
3         MyClass myClass = new MyClass();
4         myClass.normalInterfaceMethod();
5         myClass.defaultMethod1();
6         myClass.defaultMethod2();
7     }
8 }

輸出結果如下:

實現接口的默認方法
這是一個接口的私有方法
這是一個接口的私有方法

改進try-with-resources

JDK8中新增了try-with-resources語句,可以自動關閉需要關閉的資源文件。但是必須在try語句后的括號中初始化需要關閉的資源。在JDK9中改進了try-with-resources語句,你可以在try外初始化資源,然后在try后的括號中添加需要自動關的資源即可。如下示例:

在JDK8之前你只能這樣去關閉資源需要,在finally語句中定義關閉操作,如下示例:

InputStreamReader reader = null;
try {
    reader = new InputStreamReader(System.in);

    //......
} catch (IOException e) {
    e.printStackTrace();
}finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在JDK8中,新增了try-with-resources語句,你可以在try后的括號中初始化資源,可以實現資源自動關閉。如下示例:

try (InputStreamReader reader = new InputStreamReader(System.in)) {

    //......
} catch (IOException e) {
    e.printStackTrace();
}

在JDK9中,改進了try-with-resources語句,可以在try外進行初始化,在括號內引用,即可實現資源自動關閉。如下示例:

1 InputStreamReader reader = new InputStreamReader(System.in);
2 OutputStreamWriter writer = new OutputStreamWriter(System.out);
3 //多資源用分號隔開
4 try (reader;writer) {
5 
6     //......
7 } catch (IOException e) {
8     e.printStackTrace();
9 }

改進鑽石操作符

JDK9中鑽石操作符可以使用匿名實現類,可以在匿名實現類中重寫方法等操作。

Set<String> set = new HashSet<>(){
    //匿名實現類重寫add方法。
    @Override
    public boolean add(String s) {
        System.out.println("執行add方法");
        return super.add(s);
    }
};
set.add("1");

輸出結果:

執行add方法

限制使用單獨下划線標識符

在JDK8之前可以使用“_”單獨的下划線作為標識符,但在JDK9中將單獨的下划線標識符限制使用了,可能后期會將這個標識符做特殊處理如Lambda表達式一樣的->操作符一樣。如下示例:

在JDK8中可以單獨使用“_”命名

String _ = "Hello";
System.out.println(_);

輸出結果:

Hello

在JDK9中會拋出如下異常:

String 存儲結構變更

從很多不同應用程序收集的信息表名,字符串是堆使用的主要組成部分,而且,大多數字符串對象只包含一個字符,這樣的字符只需要一個字節的存儲空間,因此這些字符串對象的內部char數組中有一半的空間被閑置。

JDK9之前String底層使用char數組存儲數據private final char value[],JDK9將String底層存儲數據改為byte數組存儲數據private final byte[] value

StringBuffer和StringBuilder也同樣做了變更,將以往char數組改為byte數組。 

快速創建只讀集合

JDK9在List、Set和Map集合中新增of靜態方法,快速創建只讀集合。

在JDK9之前創建只讀集合需要如下操作:

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
//設為只讀List集合
list = Collections.unmodifiableList(list);
System.out.println(list);
Set<String> set = new HashSet<>();
set.add("E");
set.add("F");
set.add("G");
//設為只讀Set集合
set = Collections.unmodifiableSet(set);
System.out.println(set);
Map<String, String> map = new HashMap<>();
map.put("k1", "v1");
map.put("k2", "v2");
map.put("k3", "v3");
//設為只讀Map集合
map = Collections.unmodifiableMap(map);
System.out.println(map);

在JDK9中,可以使用of方法直接快速創建只讀集合,如下示例:

List<String> list = List.of("A", "B", "C");
System.out.println(list);
Set<String> set = Set.of("E", "F", "G");
System.out.println(set);
Map<String, String> map = Map.of("k1", "v1", "k2", "v2", "k3", "v3");
System.out.println(map);

上面兩則示例都輸出一樣的結果:

[A, B, C]
[E, G, F]
{k3=v3, k2=v2, k1=v1} 

增強 Stream API

JDK9在Stream接口中新增4個方法:dropWhile、takeWhile、ofNullable,為iterate方法新增重載方法。

takeWhile

takeWhile可以用於從 Stream 中獲取一部分數據,接受一個 Predicate 來進行選擇,在有序的 Stream 中,takeWhile 返回從頭開始的盡可能多的元素。

List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().takeWhile((x) -> x < 80 ).forEach(System.out::println);

返回結果:

45
43
76

從返回結果可以看出,takeWhile將會按照list集合有序的從45開始到第一個不符合條件為止的所有結果。

dropWhile

dropWhile 的方法剛好與 takeWhile想法,返回剩余的元素。

List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().dropWhile((x) -> x < 80 ).forEach(System.out::println);

輸出結果:

87
42
77
90
73
67
88

從返回結果可以看出,dropWhile方法剛好和takeWhile方法形成互補,按照list集合有序的返回從第一個不滿足條件元素開始到最后為止的所有結果。

ofNullable

在JDK8 中 Stream 不能完全為null,否則會報空指針異常。而在JDK9 中 ofNullable 方法允許創建一個為空的 Stream。

//NullPointerException
//Stream<Object> stream1 = Stream.of(null);
//System.out.println(stream1.count());
//不報異常  允許這樣寫
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());
//不報異常  允許這樣寫
List<String> list = new ArrayList<>();
list.add("A");
list.add(null);
System.out.println(list.stream().count());
//ofNullable() :允許值為 null
Stream<Object> stream = Stream.ofNullable(null);
System.out.println(stream.count());
Stream<String> stream2 = Stream.ofNullable("Hello World");
System.out.println(stream2.count());

輸出結果:

3
2
0
1

iterate() 重載方法

//JDK8 使用iterate方法,需配合limit截止。
Stream.iterate(1, (x) -> x + 1).limit(10).forEach(System.out::print);
System.out.println();
//JDK9 使用iterate的重載方法可以直接使用Predicate來截止。
Stream.iterate(1,(x) -> x <= 10, (x) -> x + 1).forEach(System.out::print);

改進 Optional 類

Optional 類是在JDK8中新增的類,主要是為了解決空指針異常。在JDK9中對這個類進行了改進,主要是新增了三個方法:stream,ifPresentOrElse 和 or 。

stream

stream方法將Optional轉為一個 Stream,如果Optional 沒有值就返回一個 Stream.empty。

List<String> list = List.of("A", "B", "C", "D", "E", "F");
Optional<List<String>> optional = Optional.of(list);
optional.stream().forEach(System.out::println);
Optional<Object> optional1 = Optional.empty();
System.out.println(optional.stream().count());

輸出結果:

[A, B, C, D, E, F]
1

ifPresentOrElse 

ifPresentOrElse方法:

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

如果 Optional 包含值,則對其包含的值調用函數 action,即 action.accept(value),這與 ifPresent 一致;與 ifPresent 方法的區別在於,ifPresentOrElse 還有第二個參數 emptyAction。如果 Optional 不包含值,那么 ifPresentOrElse 便會調用 emptyAction,即 emptyAction.run()。

//如果optional包含值,執行action.accept方法。
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
        System.out.println("沒有值."));
optional = Optional.empty();
//如果optional不包含值,執行emptyAction.run方法。
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
        System.out.println("沒有值."));

輸出結果:

Value: 1
沒有值.

or

or方法:

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)

如果Optional有值,返回 Optional 指定的值,否則返回一個預設的值。

//如果有值返回值。
Optional<String> optional1 = Optional.of("1");
Supplier<Optional<String>> supplierString = () -> Optional.of("沒有值");
optional1 = optional1.or(supplierString);
optional1.ifPresent( x -> System.out.println("Value: " + x));
//如果沒值返回預先設定的值。
optional1 = Optional.empty();
optional1 = optional1.or( supplierString);
optional1.ifPresent( x -> System.out.println("Value: " + x));

輸出結果:

Value: 1
Value: 沒有值 

多分辨率圖像 API

在 java.awt.image 包下新增了支持多分辨率圖片的API,用於支持多分辨率的圖片。

  1. 將不同分辨率的圖像封裝到一張(多分辨率的)圖像中,作為它的變體。
  2. 獲取這個圖像的所有變體。
  3. 獲取特定分辨率的圖像變體,表示一張已知分辨率單位為 DPI 的特定尺寸大小的邏輯圖像,並且這張圖像是最佳的變體。
  4. java.awt.image.MultiResolutionImage接口的基礎實現java.awt.image.BaseMultiResolutionImage獲取所需要的變體。
  5. 通過接口的getResolutionVariant(double destImageWidth, double destImageHeight)方法,根據分辨率獲取圖像。

全新的 HTTP 客服端 API

HTTP,用於傳輸網頁的協議,早在 1997 年就被采用在目前的 1.1版本中。直到 2015 年,HTTP2 才成為標准

 HTTP/1.1和HTTP/2的主要區別是如何在客戶端和服務器之間構建和傳輸數據。HTTP/1.1 依賴於請求/響應周期。 HTTP/2 允許服務器“push”數據:它可以發送比客戶端請求更多的數據。 這使得它可以優先處理並發送對於首先加載網頁至關重要的數據。

 

JDK9 中有新的方式來處理 HTTP 調用。它提供了一個新的HTTP客戶端(HttpClient),它將替代僅適用於blocking模式的HttpURLConnection(HttpURLConnection是在HTTP 1.0的時代創建的,並使用了協議無關的方法),並提供對 WebSocket 和 HTTP/2 的支持。
此外,HTTP客戶端還提供 API 來處理 HTTP/2 的特性,比如流和服務器推送等功能。
全新的 HTTP 客戶端 API 可以從 jdk.incubator.httpclient 模塊中獲取。因為在默認情況下,這個模塊是不能根據 classpath 獲取的,需要使用 add modules 命令選項配置這個模塊,將這個模塊添加到 classpath中。

智能 JAVA 編譯工具

智能 java 編譯工具( sjavac )的第一個階段始於 JEP139 這個項目,用於在多核處理器情況下提升 JDK 的編譯速度。如今,這個項目已經進入第二階段,即 JEP199,其目的是改進 Java 編譯工具,並取代目前 JDK 編譯工具 javac,繼而成為 Java 環境默認的通用的智能編譯工具。
JDK 9 還更新了 javac 編譯器以便能夠將 java 9 代碼編譯運行在低版本 Java 中。

統一的 JVM 日志系統

日志是解決問題的唯一有效途徑:曾經很難知道導致 JVM 性能問題和導致 JVM 崩潰的根本原因。不同的 JVM 日志的碎片化和日志選項(例如:JVM 組件對於日志使用的是不同的機制和規則),這使得 JVM 難以進行調試。
解決該問題最佳方法:對所有的 JVM 組件引入一個單一的系統,這些 JVM 組件支持細粒度的和易配置的 JVM 日志。

javadoc 的 HTML5 支持

JDK8 生成的java幫助文檔是在 HTML4 中。而HTML4 已經是很久的標准了。

JDK9 的javadoc,現支持HTML5 標准。

下圖是JDK8 API的HTML頁面

如下圖是JDK9 API的 HTML,右上角支持搜索功能。

java 動態編譯器

JIT(Just-in-time)編譯器可以在運行時將熱點編譯成本地代碼,速度很快。但是 Java 項目現在變得很大很復雜,因此 JIT 編譯器需要花費較長時間才能熱身完,而且有些 Java 方法還沒法編譯,性能方面也會下降。AoT 編譯就是為了解決這些問題而生的。
在 JDK 9 中, AOT(JEP 295: Ahead-of-Time Compilation)作為實驗特性被引入進來,開發者可以利用新的 jaotc 工具將重點代碼轉換成類似類庫一樣的文件。雖然仍處於試驗階段,但這個功能使得 Java應用在被虛擬機啟動之前能夠先將 Java 類編譯為原生代碼。此功能旨在改進小型和大型應用程序的啟動時間,同時對峰值性能的影響很小。
但是 Java 技術供應商 Excelsior 的營銷總監 Dmitry Leskov 擔心 AoT 編譯技術不夠成熟,希望 Oracle 能夠等到 Java 10時有個更穩定版本才發布。

 


免責聲明!

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



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