1. 程式人生 > 實用技巧 >這裡有你不得不瞭解的Java 11版本特性說明

這裡有你不得不瞭解的Java 11版本特性說明

  • 「MoreThanJava」 宣揚的是 「學習,不止 CODE」,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Java 基礎的一個總回顧,旨在 「幫助新朋友快速高質量的學習」
  • 當然 不論新老朋友 我相信您都可以 從中獲益。如果覺得 「不錯」 的朋友,歡迎 「關注 + 留言 + 分享」,文末有完整的獲取連結,您的支援是我前進的最大的動力!

Java 11 為什麼重要?

Java 11 是繼 Java 8 之後的第二個 LTS(long-term support)版本。自 Java 11 起,Oracle JDK 將不再免費提供商業用途

您可以在開發階段使用它,但要在商業上使用它,則需要購買許可證。

Java 10 是最後一個可以下載的免費 Oracle JDK。

Oracle 從 2019 年 1 月開始就停止了對 Java 8 的支援。您需要支付更多的支援費用。

如果不這樣做,雖然您可以繼續使用它,但不會獲得任何補丁/ 安全更新。

自 Java 11 起,Oracle 將不再為任何單個 Java 版本提供免費的長期支援(LTS)。

儘管 Oracle JDK 不再免費,但是您始終可以從 Oracle 或其他提供商(例如 AdoptOpenJDK,Azul,IBM,Red Hat 等..)下載 Open JDK 構建。

什麼是 LTS Module

從 2017 年開始,Oracle 和 Java 社群宣佈了向 Java 的新 6 個月節奏的轉變。它已遷移到 Oracle Java SE 產品的長期支援(LTS)模型。

LTS 版本的產品將提供 Oracle 的首要和持續的支援,目標是每三年一次。

每個 Java 版本都以一兩個主要特性為模型,這些特性驅動了版本的釋出。任何障礙都會推遲釋出和上市時間。Jigsaw 專案就是 Java 9 的一個主要特性,它多次推遲了釋出日期,並且釋出時間被推遲了超過 1.5 年。6 個月一版的發車節奏將讓特性緊隨。釋出的列車每 6 個月有一個時間表。趕上這列火車的特徵會被留下,否則他們就等下一班火車。

Oracle JDK 與 Open JDK

為了對開發人員更加友好,Oracle & Java 社群現在將 OpenJDK 二進位制檔案作為主要 JDK 進行推廣。

這與早期的 JDK 二進位制檔案是由 Oracle 專有並由 Oracle 許可的模式相比,很大程度上減輕了人們的負擔,因為 Oracle 對重新發布有各種限制。

然而,Oracle 將繼續生產他們的 JDK,但僅限於長期支援版本。這是朝著對雲和容器更友好的方向邁出的一步,因為開放 JDK 二進位制檔案可以作為容器的一部分分發。

Open JDK 的二進位制檔案每 6 個月釋出一次,而 Oracle JDK 的二進位制檔案每 3 年釋出一次(LTS版本)。

特性總覽

在瞭解完了 Java 11 附帶的負擔之後,現在讓我們作為開發人員分析 Java 11 中的重要功能。

以下是 Java 11 中的引入的部分新特性。關於 Java 11 新特性更詳細的介紹可參考這裡

  • 基於巢狀的訪問控制(JEP 181)
  • 使用單個命令執行Java檔案(JEP 330)
  • Lambda 引數的區域性變數語法(JEP 323)
  • 動態類檔案常量(JEP 309)
  • HTTP 客戶端(JEP 321)
  • Epsilon-無操作垃圾收集器(JEP 318)
  • 可擴充套件的低延遲垃圾收集器-ZGC(JEP 333)
  • Unicode 10(JEP 327)
  • 低開銷堆分析(JEP 331)
  • API 變更
  • 其他變更
    • 刪除 Java EE 和 CORBA 模組(JEP 320)
    • 飛行記錄器(JEP 328)
    • ChaCha20 和 Poly1305 加密演算法(JEP 329)
    • 改進 Aarch64 內部特徵(JEP 315)
    • 棄用 Nashorn JavaScript 引擎(JEP 335)
    • 傳輸層安全性(TLS)1.3(JEP 332)
    • 棄用 Pack200 工具和 API(JEP 336)

一. 基於巢狀的訪問控制(JEP 181)

在 Java 11 之前,從巢狀類訪問主類的 private 方法是可能的:

public class Main {
 
    public void myPublic() {
    }
 
    private void myPrivate() {
    }
 
    class Nested {

        public void nestedPublic() {
            myPrivate();
        }
    }
}

但是,如果我們使用反射,它就會給出一個 IllegalStateException

jshell> Main ob = new Main();
ob ==> Main@533ddba

jshell> import java.lang.reflect.Method;

jshell> Method method = ob.getClass().getDeclaredMethod("myPrivate");
method ==> private void Main.myPrivate()

jshell> method.invoke(ob);
|  異常錯誤 java.lang.IllegalAccessException:class REPL.$JShell$15 cannot access a member of class REPL.$JShell$11$Main with modifiers "private"
|        at Reflection.newIllegalAccessException (Reflection.java:376)
|        at AccessibleObject.checkAccess (AccessibleObject.java:647)
|        at Method.invoke (Method.java:556)
|        at (#5:1)

jshell>

這是因為 JVM 訪問規則不允許巢狀類之間進行私有訪問。我們能通過第一種方式訪問是因為 JVM 在編譯時為我們隱式地建立了私有的 橋接方法

而且這發生在幕後。這種橋接方法會稍微增加已部署應用程式的大小,並可能使使用者和工具感到困惑。

Java 11 引入巢狀訪問控制解決了這一問題。

Java 11 將巢狀的概念和相關的訪問規則引入了JVM。這簡化了 Java 原始碼編譯器的工作。

為此,類檔案格式現在包含兩個新屬性:

  1. 一個巢狀成員(通常是頂級類)被指定為巢狀主類。它包含一個屬性(NestMembers)來標識其他靜態已知的巢狀成員。
  2. 其他每個巢狀成員都有一個屬性(NestHost)來標識其巢狀主類。

因此,要使型別 C 和 D 成為巢狀夥伴,它們必須具有相同的巢狀主類。如果型別 C 在其 NestHost 屬性中列出 D,則它聲稱是 D 託管的巢狀的成員。如果 D 還在其 NestMembers 屬性中列出 C ,則將驗證成員資格。另外,型別 D 隱式為其所託管的巢狀成員。

現在,編譯器無需生成橋接方法

java.lang.Class 在反射 API 中介紹了三種方法:getNestHost()getNestMembers(),和isNestmateOf(),用於支援上述的工作。

更多請閱讀:https://www.baeldung.com/java-nest-based-access-control

二. 使用單個命令執行Java檔案(JEP 330)

該 JEP 是在學習 Java 早期階段的一個友好功能,但是在實際的 Java 開發中沒有太大的用處,我們都使用 IDE。

假設我們現在有以下的原始碼(.java 檔案):

public class HelloJava {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

在 Java 11 編譯執行需要:

$ javac HelloJava.java

$ java HelloJava

Hello World!

在 Java 11 中:

$ java HelloJava.java

Hello World!

另外,我們也可以使用 Linux Shebang 執行單個的 Java 程式:

#!/opt/java/openjdk/bin/java --source 11
public class SheBang {

    public static void main(String[] args) {

        System.out.println("Hello World!");

    }
}

這裡是在 Docker 中如此使用 Linux Shebang 執行 Java 的例子:https://mkyong.com/java/java-11-shebang-example-in-docker/

三. Lambda 引數的區域性變數語法(JEP 323)

該 JEP 是 Java 11 中唯一的語言功能的加強。

我們知道,在 Java 10 中,引入了局部變數型別推斷。因此,我們可以從 RHS 推斷出變數的型別:var list = new ArrayList<String>();

JEP 323 允許 var 用於宣告隱式型別的 Lambda 表示式的形式引數:

List<String> list = Arrays.asList("關注", "我沒有三顆心臟", "更多精彩內容分享");
String result = list.stream()
        .map((var x) -> x.toUpperCase())
        .collect(Collectors.joining(","));
System.out.println(result2);

上面與下面這個等效:

List<String> list = Arrays.asList("關注", "我沒有三顆心臟", "更多精彩內容分享");
String result = list.stream()
        .map(x -> x.toUpperCase())
        .collect(Collectors.joining(","));

這(省略型別宣告的形式)在 Java 8 中也是允許的,但是在 Java 10 中刪除了。現在,它又回到 Java 11 中以保持一致。

為什麼支援 var 來宣告隱式的 Lambda 引數呢?(特別是當我們只需跳過 Lambda 型別時)

答案是如果您想要像 @NotNull 一樣 註釋引數 時,則不能在不定義型別的情況下這樣做:

import org.jetbrains.annotations.NotNull;

List<String> list = Arrays.asList("關注", "我沒有三顆心臟", "更多精彩內容分享", null);
String result = list.stream()
  .map((@NotNull var x) -> x.toUpperCase())
  .collect(Collectors.joining(","));
System.out.println(result3);

此功能也有一定的 侷限性——您必須在所有引數上指定 var 型別,或者不指定任何型別。

在用於 Lambda 內部的引數宣告中,不可能出現以下這幾種情況:

(var s1, s2) -> s1 + s2 //no skipping allowed
(var s1, String y) -> s1 + y //no mixing allowed

var s1 -> s1 //not allowed. Need parentheses if you use var in lambda.

四. 動態類檔案常量(JEP 309)

為了使 JVM 對動態語言更具吸引力,Java SE 7 已將 invokedynamic 引入了其指令集。Java 開發人員通常不會注意到此功能,因為它隱藏在 Java 位元組碼中。

簡而言之,通過使用 invokedynamic,可以將方法呼叫的繫結延遲到第一次呼叫之前。

例如,Java 語言使用此技術來實現 Lambda 表示式,這些表示式僅在首次使用時才需要出現。

如此,invokedynamic 已發展成為一種基本的語言功能。在 constantdynamic 中,Java 11 引入了一種類似的機制,只是它延遲的是常數值的建立。

但 Java 11 本身缺少對 constantdynamic 的支援,所以這裡不做詳細贅述。

這篇文章詳細討論了該特性的目的和內部工作原理,並展示瞭如何使用 Byte Buddy 庫生成使用此新指令的程式碼,感興趣的可以閱讀一下:https://mydailyjava.blogspot.com/2018/08/hands-on-constantdynamic-class-file.html

五. HTTP 客戶端(JEP 321)

Java 11 標準化了 Http CLient API。

新的 API 支援 HTTP / 1.1 和 HTTP / 2。它旨在提高客戶端傳送請求和從伺服器接收響應的整體效能。它還原生支援 WebSockets。

下面是一個使用 Java 11 HttpClient 傳送一個簡單 GET 請求的例子:

HttpClient httpClient = HttpClient.newBuilder()
        .version(HttpClient.Version.HTTP_1_1)
        .connectTimeout(Duration.ofSeconds(10))
        .build();

HttpRequest request = HttpRequest.newBuilder()
        .GET()
        .uri(URI.create("https://www.wmyskxz.com"))
        .setHeader("User-Agent", "Java 11 HttpClient Bot")
        .build();

HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

HttpHeaders headers = response.headers();
headers.map().forEach((k, v) -> System.out.println(k + ":" + v));

System.out.println(response.statusCode());
System.out.println(response.body());

關於 HttpClient 更多內容請閱讀:https://mkyong.com/java/java-11-httpclient-examples/

六. Epsilon-無操作垃圾收集器(JEP 318)

與負責分配記憶體並釋放記憶體的 JVM GC 不同,Epsilon 僅分配記憶體。

它為以下內容分配記憶體:

  • 效能測試。
  • 記憶體壓力測試。
  • VM 介面測試。
  • 壽命極短的工作。
  • 最後一滴延遲改進。(Last-drop latency improvements.)
  • 最終吞吐量提高。

現在,Elipson 僅適用於測試環境。這將導致生產中的 OutOfMemoryError 並使應用程式崩潰。

Elipson 的好處是沒有記憶體清除開銷。因此,它將給出準確的效能測試結果,我們不再可以通過 GC 來停止它。

注意:這是一項實驗性功能。

七. 可擴充套件的低延遲垃圾收集器-ZGC(JEP 333)

Z垃圾收集器(ZGC)是可伸縮的低延遲垃圾收集器。ZGC 可以同時執行所有昂貴的工作,而不會將應用程式執行緒的執行停止超過 10 毫秒,這使得它們適合於要求低延遲和/或使用非常大的堆(數TB)的應用程式。

Z 垃圾收集器可作為實驗功能使用,可以通過命令列選項啟用 -XX:+UnlockExperimentalVMOptions -XX:+UseZGC

(ps:該垃圾收集器在 JDK 15 才生產準備就緒——JEP 377——所以可以不怎麼關注它,因為我們大概率很長一段時間都不會用上)

OpenJDK-wiki:https://wiki.openjdk.java.net/display/zgc/Main

八. Unicode 10(JEP 327)

以下是 Unicode 10.0 發行版中可用的新表情符號:

關於 Unicode 10.0 的更新,你可以在 這裡 看到詳細的內容,概括起來就是:

“ Unicode 10.0增加了8,518個字元,總共136,690個字元。這些增加包括4個新指令碼,總共139個指令碼,以及56個新表情符號字元。”

程式碼演示:

public class PrintUnicode {

    public static void main(String[] args) {
        String codepoint = "U+1F92A";   // crazy face
        System.out.println(convertCodePoints(codepoint));
    }

    // Java, UTF-16
    // Convert code point to unicode
    static char[] convertCodePoints(String codePoint) {
        Integer i = Integer.valueOf(codePoint.substring(2), 16);
        char[] chars = Character.toChars(i);
        return chars;

    }
}

好文推薦:

在 Java 中使用 Unicode 的樂趣:https://www.codetab.org/post/java-unicode-basics/

該文章詳細介紹了編碼和解碼以及 Unicode 的基礎知識,並通過 Java 程式設計詳細的展示了 Unicode 在 Java 中的使用例子。

九. 低開銷堆分析(JEP 331)

Java 虛擬機器工具介面(JVM TI)是在 Java SE 5 引入的,它可以監控 JVM 內部事件的執行,也可以控制 JVM 的某些行為,可以實現除錯、監控、執行緒分析、覆蓋率分析工具等。

該 JEP 在 JVM TI 中添加了新的低開銷的堆分析 API。

進一步閱讀:Oracle 官方 JVM TI 文件 - https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html

十. API 變更

字串新增方法

isBlank()

isBlank() ——此例項方法返回一個布林值。空字串和僅包含空格的字串將被視為空。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println(" ".isBlank()); //true
        
        String s = "wmyskxz";
        System.out.println(s.isBlank()); //false
        String s1 = "";
        System.out.println(s1.isBlank()); //true
    }
}

lines()

此方法返回字串流,它是按行分割的所有子字串的集合。

jshell> import java.util.stream.Collectors;

jshell> String str = "JD\nJD\nJD";
str ==> "JD\nJD\nJD"

jshell> System.out.println(str);
JD
JD
JD

jshell> System.out.println(str.lines().collect(Collectors.toList()));
[JD, JD, JD]

jshell>

strip() / stripLeading() / stripTrailing()

strip()——刪除字串開頭和結尾的空格。

stripLeading()——刪除字串開頭的空格。

stripTrailing()——刪除字串結尾的空格。

jshell> String str = " 我沒有三顆心臟 ";
str ==> " 我沒有三顆心臟 "

jshell> System.out.print("關注" + str.strip() + "更多精彩內容");
關注我沒有三顆心臟更多精彩內容
jshell> System.out.print("關注" + str.stripLeading() + "更多精彩內容");
關注我沒有三顆心臟 更多精彩內容
jshell> System.out.print("關注" + str.stripTrailing() + "更多精彩內容");
關注 我沒有三顆心臟更多精彩內容
jshell>

repeat(int)

repeat 方法簡單地以 int 形式將字串重複多次。

jshell> String str = "關注【我沒有三顆心臟】獲取更多精彩內容!".repeat(3);
str ==> "關注【我沒有三顆心臟】獲取更多精彩內容!關注【我沒有三顆心臟】獲取更多精彩內容!關注【我沒有三顆心臟】獲取更多精彩內容!"

jshell>

檔案讀寫字串

Java 11 致力於 String 的讀寫變得方便。它引入了以下用於讀寫檔案的方法:

  • readString();
  • writeString();

以下是程式碼示例:

Path path = Files.writeString(Files.createTempFile("test", ".txt"), "This was posted on wmyskxz.com");
System.out.println(path);
String s = Files.readString(path);
System.out.println(s); //This was posted on wmyskxz.com

十一. 其他變更

刪除 Java EE 和 CORBA 模組(JEP 320)

Java 9 中已經棄用了這些模組,現在將它們完全刪除。

下面的包被刪除:java.xml.wsjava.xml.bindjava.activationjava.xml.ws.annotationjava.corbajava.transactionjava.se.eejdk.xml.wsjdk.xml.bind

飛行記錄器(JEP 328)

Flight Recorder 以前是 Oracle JDK 中的商業附加元件,現已開放原始碼,因為 Oracle JDK 本身已不再免費。

JFR 是一種分析工具,用於從正在執行的 Java 應用程式中收集診斷資訊和分析資料。它的效能開銷可以忽略不計,通常低於 1%。因此,它可以用於生產應用。

預設情況下,JVM 禁用了 JFR,要啟動 JFR,必須使用 -XX:+FlightRecorder 選項啟動。例如,我們要啟動名為 MyApp 的應用程式:

java -XX:+ UnlockCommercialFeatures -XX:+ FlightRecorder MyApp

Java Fliight Recorder 小試牛刀 - https://juejin.im/post/6844903684912988167

ChaCha20 和 Poly1305 加密演算法(JEP 329)

Java 11 提供了 ChaCha20 和 ChaCha20-Poly1305 密碼實現。這些演算法將在 SunJCE 提供程式中實現。

有詳細瞭解需求的朋友可以參看:https://mkyong.com/java/java-11-chacha20-poly1305-encryption-examples/

改進 Aarch64 內部特徵(JEP 315)

改進現有的字串和陣列內在函式,並在 AArch64 處理器上為 java.lang.Math 包下的 sincoslog 函式實現新的內在函式。

棄用 Nashorn JavaScript 引擎(JEP 335)

Nashorn JavaScript指令碼引擎和jjs工具已被棄用,將來的發行版中可能會刪除它。

(ps:Nashorn 是在 Java 8 JEP 174 中引入,以代替 Rhino Javascript 引擎。)

傳輸層安全性(TLS)1.3(JEP 332)

Java 11 支援 RFC 8446 傳輸層安全性(TLS)1.3協議。但是,並非所有TLS 1.3功能都已實現,有關詳細資訊,請參考此 JEP 332

Java 安全套接字擴充套件(JSSE)+ TLS 1.3 示例。

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

SSLSocketFactory factory =
        (SSLSocketFactory) SSLSocketFactory.getDefault();
socket =
        (SSLSocket) factory.createSocket("google.com", 443);

socket.setEnabledProtocols(new String[]{"TLSv1.3"});
socket.setEnabledCipherSuites(new String[]{"TLS_AES_128_GCM_SHA256"});

棄用 Pack200 工具和 API(JEP 336)

該 JEP 不推薦 pack200unpack200 工具以及軟體包中的 Pack200 API java.util.jar,並且可能會在將來的版本中刪除。

(ps:Java 14 JEP 367 中刪除了 Pack200 工具和 API 。)

參考資料

  1. OpenJDK 官方說明 - http://openjdk.java.net/projects/jdk/10/
  2. Java 11 Features - https://www.journaldev.com/24601/java-11-features
  3. What is new in Java 11 - https://mkyong.com/java/what-is-new-in-java-11/
  4. Java 11 Nest Based Access Control | Baeldung - https://www.baeldung.com/java-nest-based-access-control

文章推薦

  1. 這都JDK15了,JDK7還不瞭解? - https://www.wmyskxz.com/2020/08/18/java7-ban-ben-te-xing-xiang-jie/
  2. 全網最通透的 Java 8 版本特性講解 - https://www.wmyskxz.com/2020/08/19/java8-ban-ben-te-xing-xiang-jie/
  3. Java9的這些史詩級更新你都不知道? - https://www.wmyskxz.com/2020/08/20/java9-ban-ben-te-xing-xiang-jie/
  4. 你想了解的 JDK 10 版本更新都在這裡 - https://www.wmyskxz.com/2020/08/21/java10-ban-ben-te-xing-xiang-jie/
  5. 「MoreThanJava」系列文集 - https://www.wmyskxz.com/categories/MoreThanJava/
  • 本文已收錄至我的 Github 程式設計師成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
  • 個人公眾號 :wmyskxz,個人獨立域名部落格:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!

非常感謝各位人才能 看到這裡,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!

創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!