- 「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,與您共同成長!
非常感謝各位人才能 看到這里,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!
創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!