這裡有你不得不瞭解的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 原始碼編譯器的工作。
為此,類檔案格式現在包含兩個新屬性:
- 一個巢狀成員(通常是頂級類)被指定為巢狀主類。它包含一個屬性(NestMembers)來標識其他靜態已知的巢狀成員。
- 其他每個巢狀成員都有一個屬性(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.ws
,java.xml.bind
,java.activation
,java.xml.ws.annotation
,java.corba
,java.transaction
,java.se.ee
,jdk.xml.ws
,jdk.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
包下的 sin
,cos
和 log
函式實現新的內在函式。
棄用 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 不推薦 pack200
和 unpack200
工具以及軟體包中的 Pack200
API java.util.jar
,並且可能會在將來的版本中刪除。
(ps:Java 14 JEP 367 中刪除了 Pack200 工具和 API 。)
參考資料
- OpenJDK 官方說明 - http://openjdk.java.net/projects/jdk/10/
- Java 11 Features - https://www.journaldev.com/24601/java-11-features
- What is new in Java 11 - https://mkyong.com/java/what-is-new-in-java-11/
- Java 11 Nest Based Access Control | Baeldung - https://www.baeldung.com/java-nest-based-access-control
文章推薦
- 這都JDK15了,JDK7還不瞭解? - https://www.wmyskxz.com/2020/08/18/java7-ban-ben-te-xing-xiang-jie/
- 全網最通透的 Java 8 版本特性講解 - https://www.wmyskxz.com/2020/08/19/java8-ban-ben-te-xing-xiang-jie/
- Java9的這些史詩級更新你都不知道? - https://www.wmyskxz.com/2020/08/20/java9-ban-ben-te-xing-xiang-jie/
- 你想了解的 JDK 10 版本更新都在這裡 - https://www.wmyskxz.com/2020/08/21/java10-ban-ben-te-xing-xiang-jie/
- 「MoreThanJava」系列文集 - https://www.wmyskxz.com/categories/MoreThanJava/
- 本文已收錄至我的 Github 程式設計師成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
- 個人公眾號 :wmyskxz,個人獨立域名部落格:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!
非常感謝各位人才能 看到這裡,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!
創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!