Java 18 新功能介绍
Java 18?在2022 年 3 月 22 日正式發布,Java 18 不是一個長期支持版本,這次更新共帶來 9 個新功能。
OpenJDK Java 18 下載:https://jdk.java.net/18/
OpenJDK Java 18 文檔:https://openjdk.java.net/projects/jdk/18/
| JEP | 描述 |
| JEP 400 | 默認為 UTF-8[1] |
| JEP 408 | 簡單的網絡服務器[2] |
| JEP 413 | Java API 文檔中的代碼片段[3] |
| JEP 416 | 使用方法句柄重新實現核心反射[4] |
| JEP 417 | Vector API(三次孵化)[5] |
| JEP 418 | 互聯網地址解析 SPI[6] |
| JEP 419 | Foreign Function & Memory API (二次孵化)[7] |
| JEP 420 | switch 模式匹配(二次預覽)[8] |
| JEP 421 | 棄用完成刪除[9] |
JEP 400:默認 UTF-8 字符編碼
JDK 一直都是支持 UTF-8 字符編碼,這次是把 UTF-8 設置為了默認編碼,也就是在不加任何指定的情況下,默認所有需要用到編碼的 JDK API 都使用 UTF-8 編碼,這樣就可以避免因為不同系統,不同地區,不同環境之間產生的編碼問題。
Mac OS 默認使用 UTF-8 作為默認編碼,但是其他操作系統上,編碼可能取決于系統的配置或者所在區域的設置。如中國大陸的 windows 使用 GBK 作為默認編碼。很多同學初學 Java 時可能都遇到過一個正常編寫 Java 類,在 windows 系統的命令控制臺中運行卻出現亂碼的情況。
使用下面的命令可以輸出 JDK 的當前編碼。
#?Mac?系統,默認?UTF-8 ???~?java?-XshowSettings:properties?-version?2>&1?|?grep?file.encodingfile.encoding?=?UTF-8file.encoding.pkg?=?sun.io ???~下面編寫一個簡單的 Java 程序,輸出默認字符編碼,然后輸出中文漢字 ”你好“,看看 Java 18 和 Java 17 運行區別。
系統環境:Windows 11
import?java.nio.charset.Charset;public?class?Hello{public?static?void?main(String[]?args)?{System.out.println(Charset.defaultCharset());System.out.println("你好");} }從下面的運行結果中可以看到,使用 JDK 17 運行輸出的默認字符編碼是 GBK,輸出的中文 ”你好“ 已經亂碼了;亂碼是因為 VsCode 默認的文本編輯器編碼是 UTF-8,而中國地區的 Windows 11 默認字符編碼是 GBK,也是 JDK 17 默認獲取到的編碼,所以會在控制臺輸出時亂碼;而使用 JDK 18 輸出的默認編碼就是 UTF-8,所以可以正常的輸出中文 ”你好“
JEP 408:簡單的 Web服務器
在 Java 18 中,提供了一個新命令?jwebserver,運行這個命令可以啟動一個簡單的 、最小化的靜態Web 服務器,它不支持 CGI 和 Servlet,所以最好的使用場景是用來測試、教育、演示等需求。
其實在如 Python、Ruby、PHP、Erlang 等許多平臺都提供了開箱即用的 Web 服務器,可見一個簡單的Web 服務器是一個常見的需求,Java 一直沒有這方面的支持,現在可以了。
在 Java 18 中,使用?jwebserver?啟動一個 Web 服務器,默認發布的是當前目錄。
在當前目錄創建一個網頁文件 index.html
<html> <head> <meta?http-equiv="Content-Type"?content="text/html;?charset=utf-8"?/> </head> <body> <h1>標題</h1> </body> </html>啟動?jwebserver.
???bin?./jwebserver Binding?to?loopback?by?default.?For?all?interfaces?use?"-b?0.0.0.0"?or?"-b?::". Serving?/Users/darcy/develop/jdk-18.jdk/Contents/Home/bin?and?subdirectories?on?127.0.0.1?port?8000 URL?http://127.0.0.1:8000/瀏覽器訪問:
瀏覽器訪問測試有請求時會在控制臺輸出請求信息:
127.0.0.1?-?-?[26/3月/2022:16:53:30?+0800]?"GET?/favicon.ico?HTTP/1.1"?404?- 127.0.0.1?-?-?[26/3月/2022:16:55:13?+0800]?"GET?/?HTTP/1.1"?200?-通過?help?參數可以查看?jwebserver?支持的參數。
???bin?./jwebserver?--help Usage:?jwebserver?[-b?bind?address]?[-p?port]?[-d?directory][-o?none|info|verbose]?[-h?to?show?options][-version?to?show?version?information] Options: -b,?--bind-address????-?綁定地址.?Default:?127.0.0.1?(loopback).For?all?interfaces?use?"-b?0.0.0.0"?or?"-b?::". -d,?--directory???????-?指定目錄.?Default:?current?directory. -o,?--output??????????-?Output?format.?none|info|verbose.?Default:?info. -p,?--port????????????-?綁定端口.?Default:?8000. -h,?-?,?--help????????-?Prints?this?help?message?and?exits. -version,?--version???-?Prints?version?information?and?exits. To?stop?the?server,?press?Ctrl?+?C.JEP 413:Javadoc 中支持代碼片段
在 Java 18 之前,已經支持在 Javadoc 中引入代碼片段,這樣可以在某些場景下更好的展示描述信息,但是之前的支持功能有限,比如我想高亮代碼片段中的某一段代碼是無能為力的。現在 Java 18 優化了這個問題,增加了?@snippet?來引入更高級的代碼片段。
在 Java 18 之前,使用?<pre>{@code ...}</pre>?來引入代碼片段。
/***?時間工具類* Java 18 之前引入代碼片段:*?<pre>{@code*?????public?static?String?timeStamp()?{*????????long?time?=?System.currentTimeMillis();*?????????return?String.valueOf(time?/?1000);*?????}*?}</pre>**/生成 Javadoc 之后,效果如下:
Javadoc 代碼片段高亮代碼片段
從 Java 18 開始,可以使用?@snippet?來生成注釋,且可以高亮某個代碼片段。
/***?在?Java?18?之后可以使用新的方式*?下面的代碼演示如何使用?{@code?Optional.isPresent}:*?{@snippet?:*?if?(v.isPresent())?{*?????System.out.println("v:?"?+?v.get());*?}*?}**?高亮顯示?println**?{@snippet?:*?class?HelloWorld?{*?????public?static?void?main(String...?args)?{*?????????System.out.println("Hello?World!");??????//?@highlight?substring="println"*?????}*?}*?}**/效果如下,更直觀,效果更好。
Java 18 Javadoc正則高亮代碼片段
甚至可以使用正則來高亮某一段中的某些關鍵詞:
/**?*?正則高亮:*?{@snippet?:*???public?static?void?main(String...?args)?{*???????for?(var?arg?:?args)?{?????????????????//?@highlight?region?regex?=?"\barg\b"*???????????if?(!arg.isBlank())?{*???????????????System.out.println(arg);*???????????}*???????}??????????????????????????????????????//?@end*???}*???}*/生成的 Javadoc 效果如下:
替換代碼片段
可以使用正則表達式來替換某一段代碼。
/**?*?正則替換:*?{@snippet?:*?class?HelloWorld?{*?????public?static?void?main(String...?args)?{*?????????System.out.println("Hello?World!");??//?@replace?regex='".*"'?replacement="..."*?????}*?}*?}*/這段注釋會生成如下 Javadoc 效果。
class?HelloWorld?{public?static?void?main(String...?args)?{System.out.println(...);} }附:Javadoc 生成方式
#?使用?javadoc?命令生成?Javadoc?文檔 ???bin?./javadoc?-public?-sourcepath?./src?-subpackages?com?-encoding?utf-8?-charset?utf-8?-d?./javadocout #?使用?Java?18?的?jwebserver?把生成的?Javadoc?發布測試 ???bin?./jwebserver?-d?/Users/darcy/develop/javadocout訪問測試:
JEP 416:使用方法句柄重新實現反射核心功能
Java 18 改進了?java.lang.reflect.Method、Constructor?的實現邏輯,使之性能更好,速度更快。這項改動不會改動相關 API ,這意味著開發中不需要改動反射相關代碼,就可以體驗到性能更好反射。
OpenJDK 官方給出了新老實現的反射性能基準測試結果。
Java 18 之前:
Benchmark?????????????????????????????????????Mode??Cnt???Score??Error??Units ReflectionSpeedBenchmark.constructorConst?????avgt???10??68.049?±?0.872??ns/op ReflectionSpeedBenchmark.constructorPoly??????avgt???10??94.132?±?1.805??ns/op ReflectionSpeedBenchmark.constructorVar???????avgt???10??64.543?±?0.799??ns/op ReflectionSpeedBenchmark.instanceFieldConst???avgt???10??35.361?±?0.492??ns/op ReflectionSpeedBenchmark.instanceFieldPoly????avgt???10??67.089?±?3.288??ns/op ReflectionSpeedBenchmark.instanceFieldVar?????avgt???10??35.745?±?0.554??ns/op ReflectionSpeedBenchmark.instanceMethodConst??avgt???10??77.925?±?2.026??ns/op ReflectionSpeedBenchmark.instanceMethodPoly???avgt???10??96.094?±?2.269??ns/op ReflectionSpeedBenchmark.instanceMethodVar????avgt???10??80.002?±?4.267??ns/op ReflectionSpeedBenchmark.staticFieldConst?????avgt???10??33.442?±?2.659??ns/op ReflectionSpeedBenchmark.staticFieldPoly??????avgt???10??51.918?±?1.522??ns/op ReflectionSpeedBenchmark.staticFieldVar???????avgt???10??33.967?±?0.451??ns/op ReflectionSpeedBenchmark.staticMethodConst????avgt???10??75.380?±?1.660??ns/op ReflectionSpeedBenchmark.staticMethodPoly?????avgt???10??93.553?±?1.037??ns/op ReflectionSpeedBenchmark.staticMethodVar??????avgt???10??76.728?±?1.614??ns/opJava 18 的新實現:
Benchmark?????????????????????????????????????Mode??Cnt????Score???Error??Units ReflectionSpeedBenchmark.constructorConst?????avgt???10???32.392?±?0.473??ns/op ReflectionSpeedBenchmark.constructorPoly??????avgt???10??113.947?±?1.205??ns/op ReflectionSpeedBenchmark.constructorVar???????avgt???10???76.885?±?1.128??ns/op ReflectionSpeedBenchmark.instanceFieldConst???avgt???10???18.569?±?0.161??ns/op ReflectionSpeedBenchmark.instanceFieldPoly????avgt???10???98.671?±?2.015??ns/op ReflectionSpeedBenchmark.instanceFieldVar?????avgt???10???54.193?±?3.510??ns/op ReflectionSpeedBenchmark.instanceMethodConst??avgt???10???33.421?±?0.406??ns/op ReflectionSpeedBenchmark.instanceMethodPoly???avgt???10??109.129?±?1.959??ns/op ReflectionSpeedBenchmark.instanceMethodVar????avgt???10???90.420?±?2.187??ns/op ReflectionSpeedBenchmark.staticFieldConst?????avgt???10???19.080?±?0.179??ns/op ReflectionSpeedBenchmark.staticFieldPoly??????avgt???10???92.130?±?2.729??ns/op ReflectionSpeedBenchmark.staticFieldVar???????avgt???10???53.899?±?1.051??ns/op ReflectionSpeedBenchmark.staticMethodConst????avgt???10???35.907?±?0.456??ns/op ReflectionSpeedBenchmark.staticMethodPoly?????avgt???10??102.895?±?1.604??ns/op ReflectionSpeedBenchmark.staticMethodVar??????avgt???10???82.123?±?0.629??ns/op可以看到在某些場景下性能稍微好些。
JEP 417:Vector API(三次孵化)
在 Java 16 中引入一個新的 API 來進行向量計算,它可以在運行時可靠的編譯為支持的 CPU 架構,從而實現更優的計算能力。
在 Java 17 中改進了 Vector API 性能,增強了例如對字符的操作、字節向量與布爾數組之間的相互轉換等功能。
現在在 JDK 18 中將繼續優化其性能。
JEP 418:互聯網地址解析 SPI
對于互聯網地址解析 SPI,為主機地址和域名地址解析定義一個 SPI,以便java.net.InetAddress可以使用平臺內置解析器以外的解析器。
InetAddress?inetAddress?=?InetAddress.getByName("www.wdbyte.com"); System.out.println(inetAddress.getHostAddress()); //?輸出 //?106.14.229.49JEP 419:Foreign Function & Memory API (第二次孵化)
新的 API 允許 Java 開發者與 JVM 之外的代碼和數據進行交互,通過調用外部函數,可以在不使用 JNI 的情況下調用本地庫。
這是一個孵化功能;需要添加--add-modules jdk.incubator.foreign來編譯和運行 Java 代碼,Java 18 改進了相關 API ,使之更加簡單易用。
歷史
??Java 14?JEP 370 (opens new window)[10]引入了外部內存訪問 API(孵化器)。
??Java 15?JEP 383 (opens new window)[11]引入了外部內存訪問 API(第二孵化器)。
??Java 16?JEP 389 (opens new window)[12]引入了外部鏈接器 API(孵化器)。
??Java 16?JEP 393 (opens new window)[13]引入了外部內存訪問 API(第三孵化器)。
??Java 17?JEP 412 (opens new window)[14]引入了外部函數和內存 API(孵化器)。
JEP 420:switch 表達式(二次孵化)
從 Java 17 開始,對于 Switch 的改進就已經在進行了,Java 17 的 JEP 406 已經對 Switch 表達式進行了增強,使之可以減少代碼量。
下面是幾個例子:
//?JDK?17?以前 static?String?formatter(Object?o)?{String?formatted?=?"unknown";if?(o?instanceof?Integer?i)?{formatted?=?String.format("int?%d",?i);}?else?if?(o?instanceof?Long?l)?{formatted?=?String.format("long?%d",?l);}?else?if?(o?instanceof?Double?d)?{formatted?=?String.format("double?%f",?d);}?else?if?(o?instanceof?String?s)?{formatted?=?String.format("String?%s",?s);}return?formatted; }而在 Java 17 之后,可以通過下面的寫法進行改進:
//?JDK?17?之后 static?String?formatterPatternSwitch(Object?o)?{return?switch?(o)?{case?Integer?i?->?String.format("int?%d",?i);case?Long?l????->?String.format("long?%d",?l);case?Double?d??->?String.format("double?%f",?d);case?String?s??->?String.format("String?%s",?s);default????????->?o.toString();}; }switch 可以和?null?進行結合判斷:
static?void?testFooBar(String?s)?{switch?(s)?{case?null?????????->?System.out.println("Oops");case?"Foo",?"Bar"?->?System.out.println("Great");default???????????->?System.out.println("Ok");} }case 時可以加入復雜表達式:
static?void?testTriangle(Shape?s)?{switch?(s)?{case?Triangle?t?&&?(t.calculateArea()?>?100)?->System.out.println("Large?triangle");default?->System.out.println("A?shape,?possibly?a?small?triangle");} }case 時可以進行類型判斷:
sealed?interface?S?permits?A,?B,?C?{} final?class?A?implements?S?{} final?class?B?implements?S?{} record?C(int?i)?implements?S?{}??//?Implicitly?finalstatic?int?testSealedExhaustive(S?s)?{return?switch?(s)?{case?A?a?->?1;case?B?b?->?2;case?C?c?->?3;}; }擴展:JEP 406:Switch 的類型匹配(預覽)[15]
JEP 421:棄用刪除相關
在未來將刪除 Finalization,目前 Finalization 仍默認保持啟用狀態,但是已經可以手動禁用;在未來的版本中,將會默認禁用;在以后的版本中,它將被刪除。需要進行資源管理可以嘗試?try-with-resources?或者?java.lang.ref.Cleaner。
引用鏈接
[1]?默認為 UTF-8:?https://openjdk.java.net/jeps/400
[2]?簡單的網絡服務器:?https://openjdk.java.net/jeps/408
[3]?Java API 文檔中的代碼片段:?https://openjdk.java.net/jeps/413
[4]?使用方法句柄重新實現核心反射:?https://openjdk.java.net/jeps/416
[5]?Vector API(三次孵化):?https://openjdk.java.net/jeps/417
[6]?互聯網地址解析 SPI:?https://openjdk.java.net/jeps/418
[7]?Foreign Function & Memory API (二次孵化):?https://openjdk.java.net/jeps/419
[8]?switch 模式匹配(二次預覽):?https://openjdk.java.net/jeps/420
[9]?棄用完成刪除:?https://openjdk.java.net/jeps/421
[10]?JEP 370 (opens new window):?https://openjdk.java.net/jeps/370
[11]?JEP 383 (opens new window):?https://openjdk.java.net/jeps/383
[12]?JEP 389 (opens new window):?https://openjdk.java.net/jeps/389
[13]?JEP 393 (opens new window):?https://openjdk.java.net/jeps/393
[14]?JEP 412 (opens new window):?https://openjdk.java.net/jeps/412
完
往期推薦
Spring爆出高危漏洞,嚴重程度不低于Log4J?
本周 火火火火火 的開源項目
UUID 正在被 NanoID 取代?
有道無術,術可成;有術無道,止于術
歡迎大家關注Java之道公眾號
好文章,我在看??
總結
以上是生活随笔為你收集整理的Java 18 新功能介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 垃圾oracle_第 14 章 垃圾回收
- 下一篇: Java 8 八年不倒、IntelliJ