Java9至17的新特性總結
總覽
講講Java 9-17 的一些語法糖和一些新發布的jeps, 重點講講JVM的垃圾回收器
時間線
SpringBoot 為什麼選擇Java17這個版本。我估計跟下面這個圖有關係。
Java 8 新特性
這裡簡單羅列一下Java 8 發布的jeps
1、Lambda表達式
2、函數式編程
3、介面可以添加默認方法和靜態方法,也就是定義不需要實現類實現的方法
4、方法引用
5、重複註解,同一個註解可以使用多次
6、引入Optional來避免空指針
7、引入Streams相關的API
8、引入新的Date/Time相關的API
9、新增jdeps命令行,來分析類、目錄、jar包的類依賴層級關係
10、JVM使用MetaSpace代替了永久代(PermGen Space)
Java 9 新特性
jeps
102:進程 API 更新
110:HTTP 2 客戶端 *
143:改進競爭鎖定
158:統一 JVM 日誌記錄
165:編譯器控制
193:變數句柄
197:分段程式碼快取
199:智慧 Java 編譯,第二階段
200:模組化 JDK *
201:模組化源程式碼 *
211:在導入語句中省略棄用警告
212:解決 Lint 和 Doclint 警告
213:Milling Project Coin
214:刪除 JDK 8 中已棄用的 GC 組合 * //openjdk.java.net/jeps/173
215:javac 的分層歸因
216:正確處理導入語句
217:注釋管道 2.0
219:數據報傳輸層安全性 (DTLS)
220:模組化運行時影像
221:簡化的 Doclet API
222:jshell:Java Shell(讀取-評估-列印循環)
223:新版本字元串方案
224:HTML5 Javadoc
225:Javadoc 搜索
226:UTF-8 屬性文件
227:Unicode 7.0
228:添加更多診斷命令
229:默認創建 PKCS12 密鑰庫
231:刪除啟動時 JRE 版本選擇
232:提高安全應用程式性能
233:自動生成運行時編譯器測試
235:測試由 javac 生成的類文件屬性
236:Nashorn 解析器 API
237:Linux/AArch64 埠
238:多版本 JAR 文件
240:刪除 JVM TI hprof 代理
241:刪除 jhat 工具
243:Java 級 JVM 編譯器介面
244:TLS 應用層協議協商擴展
245:驗證 JVM 命令行標誌參數
246:利用 GHASH 和 RSA 的 CPU 指令
247:為舊平台版本編譯
248:使 G1 成為默認垃圾收集器 ***
249:用於 TLS 的 OCSP 裝訂
250:在 CDS 檔案中存儲內部字元串
251:多解析度影像
252:默認使用 CLDR 區域設置數據
253:為模組化準備 JavaFX UI 控制項和 CSS API
254:緊湊字元串
255 :將選定的 Xerces 2.11.0 更新合併到 JAXP
256: BeanInfo 注釋
257:將 JavaFX/Media 更新到 GStreamer 的較新版本
258: HarfBuzz 字體布局引擎
259: Stack-Walking API
260:封裝大多數內部 API
261:模組系統 **
262:TIFF 影像 I/O
263:Windows 和 Linux 上的 HiDPI 圖形
264:平台日誌記錄 API 和服務
265:Marlin 圖形渲染器
266:更多並發更新 **
267:Unicode 8.0
268:XML 目錄
269:集合的便利工廠方法 **
270:保留關鍵部分的堆棧區域
271:統一 GC 日誌記錄
272:特定於平台的桌面功能
273:基於 DRBG 的 SecureRandom 實現
274:增強的方法句柄
275:模組化 Java 應用程式打包 **
276:語言定義的對象模型的動態鏈接
277:增強的棄用
278:G1 中巨大對象的附加測試
279:改進測試失敗故障排除
280:指示字元串連接
281:HotSpot C++ 單元測試框架
282:jlink:Java 鏈接器
283:在 Linux 上啟用 GTK 3
284:新的 HotSpot 構建系統
285:Spin-Wait 提示
287:SHA-3 哈希演算法
288:禁用 SHA-1 證書
289:棄用 Applet API
290:過濾傳入的序列化數據
291:棄用並發標記清除 (CMS) 垃圾收集器
292:在 Nashorn 中實現選定的 ECMAScript 6 功能
294:Linux/s390x 埠
295:提前編譯
297:統一 arm32/arm64 埠
298:刪除演示和示例
299:重新組織文檔
集合的便利工廠方法
Map<String, String> of = Map.of();
Map<String, Integer> e = Map.of("e", 1);
Map<String, Integer> e1 = Map.ofEntries(Map.entry("e", 1));
Set<String> set = Set.of("e");
List<App> list = List.of();
響應式編程
Flow API 是 Java 9 引入的響應式編程的介面 RXJava
java.util.concurrent.Flow;
Publisher:發布者,負責發布消息 定義了生產數據和控制事件的方法;
Subscriber:訂閱者,負責訂閱處理消息 定義了消費數據和事件的方法 ;
Subscription:訂閱控制類,可用於發布者和訂閱者之間通訊 定義了鏈接Publisher和Subscriber的方法;
Processor:處理者,同時充當Publisher和Subscriber的角色 定義了轉換Publisher到Subscriber的方法
class SubmissionPublisher<T>是Flow.Publisher<T>的實現,她可以靈活的生產數據,同時與Reactive Stream兼容。
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
publisher.consume(v -> System.out.println("consumer:" + v));
publisher.submit("1");
publisher.submit("2");
publisher.submit("3");
publisher.submit("4");
publisher.offer("3",(d,v)->{
System.out.println("droped");
return true;
});
新的HTTP API
JDK 9 已經完成了一些原型設計工作,於JDK10發布
設計目標
必須易於用於常見情況,包括簡單的阻塞模式。
必須提供事件通知,例如「收到的標頭」、錯誤和「收到的響應正文」。此通知不一定基於回調,但可以使用 CompletableFuture 等非同步機制。
一個簡單而簡潔的 API,可滿足 80-90% 的應用程式需求。這可能意味著相對較小的 API 佔用空間,不一定會暴露協議的所有功能。
必須向伺服器公開 HTTP 協議請求的所有相關方面,以及來自伺服器的響應(標頭、正文、狀態程式碼等)。
必須支援標準和通用的身份驗證機制。這最初將僅限於基本身份驗證。
必須能夠輕鬆設置 WebSocket 握手。
必須支援HTTP/2。(HTTP/2 的應用程式級語義與 1.1 基本相同,儘管有線協議完全不同。)
必須能夠協商從 1.1 升級到 2(或不升級),或者從一開始就選擇 2。
必須支援伺服器推送,即伺服器無需客戶端明確請求即可向客戶端推送資源的能力。
必須執行與現有網路 API 一致的安全檢查。
應該對 lambda 表達式等新的語言特性友好。
應該對嵌入式系統要求友好,特別是避免永久運行的計時器執行緒。
必須支援 HTTPS/TLS。
HTTP/1.1 的性能要求:
性能必須與現有HttpURLConnection 實施相提並論。
當用作客戶端 API 時,性能必須與 Apache HttpClient 庫以及 Netty 和 Jetty 相當。
新 API 的記憶體消耗必須與HttpURLConnectionApache HttpClient 以及 Netty 和 Jetty 用作客戶端 API 的記憶體消耗相當或更低。
HTTP/2 的性能要求:
儘管有任何平台限制(例如,TCP 段確認窗口),性能必須在新協議所期望的方面(即在可擴展性和延遲方面)優於 HTTP/1.1。
當用作 HTTP/2 的客戶端 API 時,性能必須與 Netty 和 Jetty 相當。
HttpURLConnection新 API 的記憶體消耗必須與使用、Apache HttpClient 以及用作客戶端 API 時的 Netty 和 Jetty相同或更低。
性能比較只會在可比較的操作模式的上下文中進行,因為新的 API 將強調簡單性和易用性,而不是涵蓋所有可能的用例,
這項工作是為 JDK 9 設計的。Java EE 在 Servlet 4.0 API 中的 HTTP/2 實現中可能會重用一些程式碼,
因此僅使用 JDK 8 語言功能,並在可能的情況下使用 API。
其目的是利用在 JDK 9 中使用 API 的經驗,可以在 JDK 10 中的 java.net 命名空間下標準化 Java SE 中的 API。
當這種情況發生時,作為未來 JEP 的一部分,API將不再作為孵化器模組存在。
模組化
JDK模組化
應用程式模組化
通過定義module-info.java來控制Java的模組化
open module feature { //open 開放整個模組 為了運行時的反射
requires xxx; // 需要使用該模組
requires transitive xxx; //繼承xxx的隱式可讀
requires static jdk.internal.vm.ci; //編譯期檢查該模組是否已存在
exports xxxx;
exports xxx to xxx; //限制導出 to 後面的模組才能訪問他
//服務發現 ServiceLoader.load(interface.class);
provides Interface with Impl; //給interface 提供一個服務Impl
uses Interface;//使用該interface 通過ServiceLoader 無需配置META-INFO
opens jdk9.http2; //其他模組可以進行深度反射
opens jdk9 to java.base; //只能由該模組可以進行深度反射
}
vm 參數
--illegal-access=permit //運行反射
--add-opens
類載入器
JDK9後
- 引導類載入器:定義核心Java SE和JDK模組。
- 平台類載入器:定義部分Java SE和JDK模組。
- 應用或系統類載入器:定義CLASSPATH上的類和模組路徑中的模組。
JDK9之前
文件流
var classLoader = ClassLoader.getSystemClassLoader();
InputStream in = classLoader.getResourceAsStream("file");
try (var os = new FileOutputStream("file2")){
in.transferTo(os);
}
in.close();
Java 10 新特性
jeps
286:本地變數類型推斷 *
296:將 JDK 整合到一個存儲庫中
304:垃圾收集器介面
307:G1 的並行Full GC *
310:應用程式類數據共享
312:執行緒本地握手
313:刪除 Native-Header生成工具 (javah)
314:附加 Unicode 語言標籤擴展
316:替代存儲設備上的堆分配
317:基於 Java 的實驗性 JIT 編譯器 Graal *
319:根證書
322:基於時間的發布版本控制
新語法糖
var ss ="111";
var list = List.of();
var map = Map.of();
var list2 = List.copyOf(list);
var app = new VarApp();
var set = Set.of();
for (var i = 0; i <10; i++) {
System.out.println(i);
}
System.out.println(int.class.getName());
Stream<Integer> integerStream = Stream.of(3, 5, 6, 4, 5);
LocalDate of = LocalDate.of(2022, 2, 28);
//java9
// 遇到 不滿足條件就跳出循環
System.out.println("遇到 不滿足條件就跳出循環》》》》》》》》》》》》");
integerStream.takeWhile(t->t%2!=0).forEach(System.out::println);
System.out.println("只要碰到滿足的就把後面的數據留下來");
integerStream = Stream.of(3, 4, 6, 4, 5,3);
integerStream.dropWhile(t->t%2!=0).forEach(System.out::println);
//流迭代
Stream.iterate(2,n->n+1).limit(10).forEach(System.out::println);
Stream.iterate(2,t->t<100,n->n+1).forEach(System.out::println);
GraalVM
GraalVM想成為一統天下的「最終」虛擬機
Java 11 新特性
jeps
181:基於嵌套的訪問控制
309:動態類文件常量
315:改進 Aarch64 內在函數
318:Epsilon:無操作垃圾收集器
320:刪除 Java EE 和 CORBA 模組
321:HTTP 客戶端(標準) *
323:var 變數Lambda 參數的語法 *
324:與 Curve25519 和 Curve448 的密鑰協議
327:Unicode 10
328:JFR JVM監控 ***
329:ChaCha20 和 Poly1305 加密演算法
330:啟動單文件源程式碼程式
331:低開銷堆分析
332:傳輸層安全 (TLS) 1.3
333:ZGC:可擴展的低延遲垃圾收集器(實驗性) *
335:棄用 Nashorn JavaScript 引擎
336:棄用 Pack200 工具和 API
HTTP API
HTTP 客戶端是在 Java 11 中添加的。它可用於通過網路請求 HTTP 資源。
它支援 同步和非同步編程模型的HTTP/1.1和HTTP/2,將請求和響應主體作為響應流處理,並遵循熟悉的構建器模式。
HttpClient client = HttpClient.newHttpClient();
client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.NORMAL)
.proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
.authenticator(Authenticator.getDefault())
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("//baidu.com/"))
.build();
request = HttpRequest.newBuilder()
.uri(URI.create("//openjdk.java.net/"))
.timeout(Duration.ofMinutes(1))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{name:\"wang\",age:18}"))
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
JFR
原生jvm事件流監控
Configuration config = Configuration.getConfiguration("default");
try (var es = new RecordingStream(config)) {
es.onEvent("jdk.GarbageCollection", System.out::println);
es.onEvent("jdk.CPULoad", System.out::println);
es.onEvent("jdk.JVMInformation", System.out::println);
es.setMaxAge(Duration.ofSeconds(10));
es.start();
}
Optional
Optional<String> s = Optional.ofNullable(null);
//or stream orElseThrow isEmpty ifPresentOrElse
String c = s.or(() -> Optional.of("c")).get();
System.out.println(c);
Order order = new Order();
Optional<Store> storeOptional = Optional.ofNullable(null);
storeOptional.ifPresentOrElse(k->{
order.storeName=k.name;
},
()->{
order.storeName ="未知";
}
);
String
System.out.println("".isBlank());
// 取前後的空格
System.out.println(" java ".strip());
// 取前的空格
System.out.println(" java".stripLeading());
// 取後的空格
System.out.println("java ".stripTrailing());
//複製字元串
System.out.println("java".repeat(2));
//獲取行數
System.out.println("java\n java\n java\n".lines().count());
Java 12 新特性
jeps
189: Shenandoah:一種低暫停時間的垃圾收集器(實驗性)
230: 微基準套件
325: swtich表達式(預覽)
334: JVM 常量 API
340: 一個 AArch64 埠,不是兩個
341: 默認 CDS 檔案
344: G1 的可中止混合集合
346: 立即從 G1 返回未使用的已提交記憶體
語法糖
public static void main(String[] args) {
int day = 1;
Integer integer = get(day);
System.out.println(integer);
double mean = Stream.of(1, 2, 3, 4, 5).collect(Collectors.teeing(
//總和
Collectors.summingDouble(i -> i),
//元素數量
Collectors.counting(),
//處理結果
(sum, n) -> sum / n
));
String ms = "app";
String transform = ms.transform(t -> t + "aa");
System.out.println(transform);
}
public static int get(int day) {
return switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> 0;
};
}
Java 13 新特性
JEPS
350: 動態 CDS 檔案
351: ZGC:取消提交未使用的記憶體
353: 重新實現 Legacy Socket API
354: 切換表達式(預覽)
355: 文本塊(預覽)
文本塊
String html1=" <html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
" </html>";
System.out.println(html1);
String html =
"""
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
System.out.println(html);
Java 14 新特性
JEPS
305: instanceof 的模式匹配(預覽版)
343: 包裝工具(孵化器)
345: G1 的 NUMA 感知記憶體分配
349: JFR 事件流
352: 非易失性映射位元組緩衝區
358: 有用的 NullPointerExceptions
359: record(預覽)
361: Switch表達式(標準)
362: 棄用 Solaris 和 SPARC 埠
363: 刪除並發標記清除 (CMS) 垃圾收集器
364: macOS 上的 ZGC
365: Windows 上的 ZGC
366: 棄用 ParallelScavenge + SerialOld GC 組合
367: 刪除 Pack200 工具和 API
368: 文本塊(第二次預覽)
370: Foreign-Memory Access API(孵化器)
record
record Person<T>(String name,Integer age){
public List<T> getList(){
return List.of();
}
}
instanceof
public static void getList(Object o) {
if (o instanceof String msg) {
System.out.println(msg.length());
}
}
空指針錯誤
B b = new Main.B();
System.out.println(b.a.c);
/**
* Cannot read field "c" because "b.a" is null
* at jdk14.Main.main(Main.java:14)
*/
Java 15 新特性
jeps
339: 愛德華茲曲線數字簽名演算法 (EdDSA)
360: 密封類(預覽版)
371: 隱藏類
372: 移除 Nashorn JavaScript 引擎
373: 重新實現舊版 DatagramSocket API
374: 禁用和棄用偏向鎖定
375: instanceof 的模式匹配(第二次預覽)**
377: ZGC:可擴展的低延遲垃圾收集器 **
378: 文本塊 **
379: Shenandoah:一個低暫停時間的垃圾收集器 **
381: 刪除 Solaris 和 SPARC 埠
383: Foreign-Memory Access API(第二個孵化器)
384: record(第二次預覽)
385: 棄用 RMI 激活以進行刪除
密封類
/**
* 密封類對其允許的子類(由其permits子句指定的類)施加三個約束:
*
* 密封類及其允許的子類必須屬於同一個模組,並且如果在未命名的模組中聲明,則必須屬於同一個包。
*
* 每個允許的子類都必須直接擴展密封類。
*
* 每個允許的子類都必須選擇一個修飾符來描述它如何繼續由其超類發起的密封:
*
* public 可以訂製那些類可以被繼承或實現
*/
public sealed abstract class Shape permits Circle,Rectangle,Square {
public non-sealed class Square extends Shape{
public class SquareQA extends Square{
public sealed class Rectangle extends Shape permits FilledRectangle {
public final class FilledRectangle extends Rectangle{
public final class Circle extends Shape{
Java 16 新特性
338: 矢量 API(孵化器)
347: 啟用 C++14 語言功能
357: 從 Mercurial 遷移到 Git
369: 遷移到 GitHub
376: ZGC:並發執行緒堆棧處理
380: Unix 域套接字通道
386: 高山 Linux 埠
387: 彈性元空間
388: Windows/AArch64 埠
389: 外部鏈接器 API(孵化器)
390: 基於值的類的警告
392: 打包工具
393: Foreign-Memory Access API(第三孵化器)
394: instanceof 的模式匹配
395: record
396: 默認情況下對 JDK 內部進行強封裝
397: 密封課程(第二次預覽)
Java 17 新特性
306: 恢復始終嚴格的浮點語義
356: 增強的偽隨機數生成器
382: 新的 macOS 渲染管道
391: macOS/AArch64 埠
398: 棄用 Applet API 以進行刪除
403: 強烈封裝 JDK 內部
406: 開關的模式匹配(預覽版)
407: 刪除 RMI 激活
409: 密封類
410: 刪除實驗性 AOT 和 JIT 編譯器 Graal
411: 棄用安全管理器以進行刪除
412: 外部函數和記憶體 API(孵化器)
414: Vector API(第二個孵化器)
415: 特定於上下文的反序列化過濾器
Java 9 模組化
概念
- 模組化(modularization)是指將系統分解成獨立且相互鏈接的木刻的行為
- 模組(module)是包含程式碼的可識別工件, 使用了元數據來描述模組及其與其他模組的關係
為什麼引入模組化
- 減少環境資源開銷,jlink打包後會打出最小鏡像
- 可配置的封裝隔離機制 通過module-info
模組的核心原則
- 強封裝性
- 一個模組必須能夠對其他模組隱藏其部分程式碼。這樣一來,就可以在可公開使用的程式碼和被視為內部實現細節的程式碼之間劃定一條清晰的界限,從而防止模組之間發生意外或不必要的耦合,即無法使用被封裝的內容。
- 定義良好介面
- 雖然封裝是很好的做法,但如果模組需要一起工作,那麼就不能將所有的內容都進行封裝。從定義上講,沒有封裝的程式碼是模組公共API的一部分。由於其他模組可以使用這些公共程式碼,因此必須非常小心地管理它們。未封裝程式碼中任何一個重大更改都可能會破壞依賴該程式碼的其他模組。因此,模組應該向其他模組公開定義良好且穩定的介面。
- 顯式依賴
- 一個模組通常需要使用其他模組來完成自己的工作,這些依賴關係必須是模組定義的一部分,以便使模組能夠獨立運行。
截止到Jdk17的GC的垃圾回收器的發展史
//todo