大家好,我是風箏,公眾號「古時的風箏」,專注於 Java技術 及周邊生態。
文章會收錄在 JavaNewBee 中,更有 Java 後端知識圖譜,從小白到大牛要走的路都在裡面。
最近在調研 JDK 17,並且試著將之前的一個小專案升級了一下,在測試環境跑了一段時間。最終,決定了,新專案要採用 JDK 17 了。
JDK 1.8:“不是說好了,他發任他發,你用 Java 8 嗎?”
不光是我呀,連 Spring Boot 都開始要擁護 JDK 17了,下面這一段是 Spring Boot 3.0 的更新日誌。
Spring Boot 3.0 requires Java 17 as a minimum version. If you are currently using Java 8 or Java 11, you'll need to upgrade your JDK before you can develop Spring Boot 3.0 applications.
Spring Boot 3.0 需要 JDK 的最低版本就是 JDK 17,如果你想用 Spring Boot 開發應用,你需要將正在使用的 Java 8 或 Java 11升級到 Java 17。
選用 Java 17,概括起來主要有下面幾個主要原因:
1、JDK 17 是 LTS (長期支援版),可以免費商用到 2029 年。而且將前面幾個過渡版(JDK 9-JDK 16)去其糟粕,取其精華的版本;
2、JDK 17 效能提升不少,比如重寫了底層 NIO,至少提升 10% 起步;
3、大多數第三方框架和庫都已經支援,不會有什麼大坑;
4、準備好了,來吧。
拿幾個比較好玩兒的特性來說一下 JDK 17 對比 JDK 8 的改進。
密封類
密封類應用在介面或類上,對介面或類進行繼承或實現的約束,約束哪些型別可以繼承、實現。例如我們的專案中有個基礎服務包,裡面有一個父類,但是介於安全性考慮,值允許專案中的某些微服務模組繼承使用,就可以用密封類了。
沒有密封類之前呢,可以用 final
關鍵字約束,但是這樣一來,被修飾的類就變成完全封閉的狀態了,所有類都沒辦法繼承。
密封類用關鍵字 sealed
修飾,並且在宣告末尾用 permits
表示要開放給哪些型別。
下面宣告瞭一個叫做 SealedPlayer
的密封類,然後用關鍵字 permits
將整合許可權開放給了 MarryPlayer
類。
public sealed class SealedPlayer permits MarryPlayer {
public void play() {
System.out.println("玩兒吧");
}
}
之後 MarryPlayer
就可以繼承 SealedPlayer
了。
public non-sealed class MarryPlayer extends SealedPlayer{
@Override
public void play() {
System.out.println("不想玩兒了");
}
}
繼承類也要加上密封限制。比如這個例子中是用的 non-sealed
,表示不限制,任何類都可以繼承,還可以是 sealed
,或者 final
。
如果不是 permits 允許的型別,則沒辦法繼承,比如下面這個,編譯不過去,會給出提示 "java: 類不得擴充套件密封類:org.jdk17.SealedPlayer(因為它未列在其 'permits' 子句中)"
public non-sealed class TomPlayer extends SealedPlayer {
@Override
public void play() {
}
}
空指標異常
String s = null;
String s1 = s.toLowerCase();
JDK1.8 的版本下執行:
Exception in thread "main" java.lang.NullPointerException
at org.jdk8.App.main(App.java:10)
JDK17的版本(確切的說是14及以上版本)
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toLowerCase()" because "s" is null
at org.jdk17.App.main(App.java:14)
出現異常的具體方法和原因都一目瞭然。如果你的一行程式碼中有多個方法、多個變數,可以快速定位問題所在,如果是 JDK1.8,有些情況下真的不太容易看出來。
yield關鍵字
public static int calc(int a,String operation){
var result = switch (operation) {
case "+" -> {
yield a + a;
}
case "*" -> {
yield a * a;
}
default -> a;
};
return result;
}
換行文字塊
如果你用過 Python,一定知道Python 可以用 'hello world'
、"hello world"
、''' hello world '''
、""" hello world """
四種方式表示一個字串,其中後兩種是可以直接支援換行的。
在 JDK 1.8 中,如果想宣告一個字串,如果字串是帶有格式的,比如回車、單引號、雙引號,就只能用轉義符號,例如下面這樣的 JSON 字串。
String json = "{\n" +
" \"name\": \"古時的風箏\",\n" +
" \"age\": 18\n" +
"}";
從 JDK 13開始,也像 Python 那樣,支援三引號字串了,所以再有上面的 JSON 字串的時候,就可以直接這樣宣告瞭。
String json = """
{
"name": "古時的風箏",
"age": 18
}
""";
record記錄類
類似於 Lombok 。
傳統的Java應用程式透過建立一個類,透過該類的構造方法例項化類,並透過getter和setter方法訪問成員變數或者設定成員變數的值。有了record關鍵字,你的程式碼會變得更加簡潔。
之前宣告一個實體類。
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用 Record
類之後,就像下面這樣。
public record User(String name) {
}
呼叫的時候像下面這樣
RecordUser recordUser = new RecordUser("古時的風箏");
System.out.println(recordUser.name());
System.out.println(recordUser.toString());
輸出結果
Record 類更像是一個實體類,直接將構造方法加在類上,並且自動給欄位加上了 getter 和 setter。如果一直在用 Lombok 或者覺得還是顯式的寫上 getter 和 setter 更清晰的話,完全可以不用它。
G1 垃圾收集器
JDK8可以啟用G1作為垃圾收集器,JDK9到 JDK 17,G1 垃圾收集器是預設的垃圾收集器,G1是兼顧老年代和年輕代的收集器,並且其記憶體模型和其他垃圾收集器是不一樣的。
G1垃圾收集器在大多數場景下,其效能都好於之前的垃圾收集器,比如CMS。
ZGC
從 JDk 15 開始正式啟用 ZGC,並且在 JDK 16後對 ZGC 進行了增強,控制 stop the world 時間不超過10毫秒。但是預設的垃圾收集器仍然是 G1。
配置下面的引數來啟用 ZGC 。
-XX:+UseZGC
可以用下面的方法檢視當前所用的垃圾收集器
JDK 1.8 的方法
jmap -heap 8877
JDK 1.8以上的版本
jhsdb jmap --heap --pid 8877
例如下面的程式採用 ZGC 垃圾收集器。
其他一些小功能
1、支援 List.of()、Set.of()、Map.of()和Map.ofEntries()等工廠方法例項化物件;
2、Stream API 有一些改進,比如 .collect(Collectors.toList())
可以直接寫成 .toList()
了,還增加了 Collectors.teeing()
,這個挺好玩,有興趣可以看一下;
3、HttpClient
重寫了,支援 HTTP2.0,不用再因為嫌棄 HttpClient 而使用第三方網路框架了,比如OKHTTP;
升級 JDK 和 IDEA
安裝 JDK 17,這個其實不用說,只是推薦一個網站,這個網站可以下載各種系統、各種版本的 JDK 。地址是 https://adoptium.net/。
還有,如果你想在 IDEA 上使用 JDK 17,可能要升級一下了,只有在 2021.02
版本之後才支援 JDK 17。
如果覺得還不錯的話,給個推薦吧!
公眾號「古時的風箏」,Java 開發者,專注 Java 及周邊生態。堅持原創乾貨輸出,你可選擇現在就關注我,或者看看歷史文章再關注也不遲。長按二維碼關注,跟我一起變優秀!