SpringBoot 3.0 最低版本要求的JDK 17,這幾個新特性不能不知道!
最近,有很多人在傳說 SpringBoot要出3.0的版本了,并且宣布不再支持 Java 8,最低要求是 Java 17了。
其實,早在2021年9月份,關(guān)于 Spring Framework 6.0的消息出來的時候,Spring 官方就已經(jīng)明確了不會向下兼容,最低的 JDK 版本是 JDK 17。
2022年,Spring Framework 6.0和 SpringBoot 3.0都會推出,在此之前,Java社區(qū)很堅挺,一直是"新版任你發(fā),我用Java 8",不管新版本怎么出,很少有人愿意升級。
這一次,Spring 直接來了個大招,跨過 JDK 8-16,直接升級到 JDK 17 ,不知道會對 Java生態(tài)產(chǎn)生怎樣的影響。
為什么是 Java 17
這么多新版本的 JDK,而且2022年還會推出 JDK 18 和 JDK 19,為什么 Spring 選擇了 JDK 17呢。主要是因為他是一個 LTS版本,所謂 LTS,是 Long Term Support,也就是官方保證會長期支持的版本。從 JDK 誕生到現(xiàn)在,還在長期支持的版本主要有 JDK 7、JDK 8 、JDK 11以及 JDK 17
這一次 Spring直接跨越了 JDK 11,升級到 JDK 17,主要的考慮應(yīng)該是因為JDK 17有更多的新特性支持。
接下來我們介紹幾個新特性,這些新特性都是我們開發(fā)者息息相關(guān)的,或者說是會影響我們寫代碼的。
JDK 17 支持的新特性
這里所謂的新特性,不只是 JDK 17中新增的,而是 JDK 17和 JDK 8相比,新增的特性。
本地變量類型推斷
在Java 10之前版本中,我們想定義定義局部變量時。我們需要在賦值的左側(cè)提供顯式類型,并在賦值的右邊提供實現(xiàn)類型:
MyObject value = new MyObject();
在Java 10中,提供了本地變量類型推斷的功能,可以通過var聲明變量:
var value = new MyObject();
本地變量類型推斷將引入“var”關(guān)鍵字,而不需要顯式的規(guī)范變量的類型。其實,所謂的本地變量類型推斷,也是Java 10提供給開發(fā)者的語法糖。雖然我們在代碼中使用var進行了定義,但是對于虛擬機來說他是不認識這個var的,在java文件編譯成class文件的過程中,會進行解糖,使用變量真正的類型來替代var(詳細信息可以參考:我反編譯了Java 10的本地變量類型推斷)
Switch 表達式
在JDK 12中引入了Switch表達式作為預(yù)覽特性。并在Java 13中修改了這個特性,引入了yield語句,用于返回值。而在之后的Java 14中,這一功能正式作為標準功能提供出來。在以前,我們想要在switch中返回內(nèi)容,還是比較麻煩的,一般語法如下:
int i;
switch (x) {
case "1":
i=1;
break;
case "2":
i=2;
break;
default:
i = x.length();
break;
}
在JDK13中使用以下語法:
int i = switch (x) {
case "1" -> 1;
case "2" -> 2;
default -> {
int len = args[1].length();
yield len;
}
};
或者
int i = switch (x) {
case "1": yield 1;
case "2": yield 2;
default: {
int len = args[1].length();
yield len;
}
};
在這之后,switch中就多了一個關(guān)鍵字用于跳出switch塊了,那就是yield,他用于返回一個值。和return的區(qū)別在于:return會直接跳出當前循環(huán)或者方法,而yield只會跳出當前switch塊。
Text Blocks
Java 13中提供了一個Text Blocks的預(yù)覽特性,并且在Java 14中提供了第二個版本的預(yù)覽。text block,文本塊,是一個多行字符串文字,它避免了對大多數(shù)轉(zhuǎn)義序列的需要,以可預(yù)測的方式自動格式化字符串,并在需要時讓開發(fā)人員控制格式。我們以前從外部copy一段文本串到Java中,會被自動轉(zhuǎn)義,如有一段以下字符串:
<html>
<body>
<p>Hello, world</p>
</body>
</html>
將其復(fù)制到Java的字符串中,會展示成以下內(nèi)容:
"<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
即被自動進行了轉(zhuǎn)義,這樣的字符串看起來不是很直觀,在JDK 13中,就可以使用以下語法了:
"""
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
使用“”“作為文本塊的開始符和結(jié)束符,在其中就可以放置多行的字符串,不需要進行任何轉(zhuǎn)義??雌饋砭褪智逅恕H绯R姷腟QL語句:
String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;
""";
看起來就比較直觀,清爽了。
Records
Java 14 中便包含了一個新特性:EP 359: Records,Records的目標是擴展Java語言語法,Records為聲明類提供了一種緊湊的語法,用于創(chuàng)建一種類中是“字段,只是字段,除了字段什么都沒有”的類。通過對類做這樣的聲明,編譯器可以通過自動創(chuàng)建所有方法并讓所有字段參與hashCode()等方法。這是JDK 14中的一個預(yù)覽特性。使用record關(guān)鍵字可以定義一個記錄:
record Person (String firstName, String lastName) {}
record 解決了使用類作為數(shù)據(jù)包裝器的一個常見問題。純數(shù)據(jù)類從幾行代碼顯著地簡化為一行代碼。(詳見:Java 14 發(fā)布了,不使用”class”也能定義類了?還順手要干掉Lombok!)
封閉類
在Java 15之前,Java認為"代碼重用"始終是一個終極目標,所以,一個類和接口都可以被任意的類實現(xiàn)或繼承。但是,在很多場景中,這樣做是容易造成錯誤的,而且也不符合物理世界的真實規(guī)律。例如,假設(shè)一個業(yè)務(wù)領(lǐng)域只適用于汽車和卡車,而不適用于摩托車。在Java中創(chuàng)建Vehicle抽象類時,應(yīng)該只允許Car和Truck類擴展它。通過這種方式,我們希望確保在域內(nèi)不會出現(xiàn)誤用Vehicle抽象類的情況。為了解決類似的問題,在Java 15中引入了一個新的特性——密閉。想要定義一個密閉接口,可以將sealed修飾符應(yīng)用到接口的聲明中。然后,permit子句指定允許實現(xiàn)密閉接口的類:
public sealed interface Service permits Car, Truck {
}
以上代碼定義了一個密閉接口Service,它規(guī)定只能被Car和Truck兩個類實現(xiàn)。與接口類似,我們可以通過使用相同的sealed修飾符來定義密閉類:
public abstract sealed class Vehicle permits Car, Truck {
}
通過密閉特性,我們定義出來的Vehicle類只能被Car和Truck繼承。
instanceof 模式匹配
instanceof是Java中的一個關(guān)鍵字,我們在對類型做強制轉(zhuǎn)換之前,會使用instanceof做一次判斷,例如:
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.miaow();
} else if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
Java 14帶來了改進版的instanceof操作符,這意味著我們可以用更簡潔的方式寫出之前的代碼例子:
if (animal instanceof Cat cat) {
cat.miaow();
} else if(animal instanceof Dog dog) {
dog.bark();
}
我們都不難發(fā)現(xiàn)這種寫法大大簡化了代碼,省略了顯式強制類型轉(zhuǎn)換的過程,可讀性也大大提高了。
switch 模式匹配
基于instanceof模式匹配這個特性,我們可以使用如下方式來對對象o進行處理:
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;
}
可以看到,這里使用了很多if-else,其實,Java中給我們提供了一個多路比較的工具,那就是switch,而且從Java 14開始支持switch表達式,但switch的功能一直都是非常有限的。在Java 17中,Java的工程師們擴展了switch語句和表達式,使其可以適用于任何類型,并允許case標簽中不僅帶有變量,還能帶有模式匹配。我們就可以更清楚、更可靠地重寫上述代碼,例如:
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處理的是一個Object類型,而且case中也不再是精確的值匹配,而是模式匹配了。
總結(jié)以上,我們介紹了幾個從 JDK 9開始,一直到 JDK 17中的幾個能夠改變我們寫代碼的方式的新特性。其實,眾多的版本中,還有一些其他的特性及優(yōu)化,我們沒有在這里一一展開。大家感興趣的可以到 JDK 官網(wǎng)查看各個版本的新功能介紹。隨著 Spring Framework 6 和 SpringBoot 3.0的推出,相信會有一些公司在新項目中采用新版本,那么 JDK 17勢必要被應(yīng)用到生產(chǎn)環(huán)境中。以上這些特性,大多數(shù)都是對開發(fā)比較友好的,有機會的話可以應(yīng)用起來。