掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
盡管Java 11是一個(gè)長(zhǎng)期支持版本,且被廣泛應(yīng)用于許多應(yīng)用程序中,但這里有些重要的理由需要考慮遷移到Java 17:8

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名注冊(cè)、虛擬主機(jī)、營(yíng)銷軟件、網(wǎng)站建設(shè)、博野網(wǎng)站維護(hù)、網(wǎng)站推廣。
Java 17引入了一些改進(jìn)和新功能,這些功能將得到長(zhǎng)期支持。
Java引入了文本塊功能,這使代碼更易讀,并避免了不必要的字符串格式化操作?,F(xiàn)在,我們可以將文本放在三引號(hào)之間,并在其中包含多個(gè)雙引號(hào)字符串,而無(wú)需使用轉(zhuǎn)義字符。下面是一個(gè)示例:
private static void jsonBlock() {
String text = """
{
"name": "John Doe",
"age": 45,
"address": "Doe Street, 23, Java Town"
}
""";
System.out.println(text);
}正如所看到的,這樣可以很容易地編寫需要大量使用轉(zhuǎn)義字符的Json和類似的字符串。
此外,結(jié)尾的三個(gè)雙引號(hào)表示文本塊的開始或其在輸出中的縮進(jìn)。在上面的示例中,輸出中每行的位置都比最后一個(gè)字符后面的雙引號(hào)向后兩個(gè)空格。
引入了兩個(gè)新的轉(zhuǎn)義字符,'\s'用于添加空格,'\n'用于刪除換行符。在編寫長(zhǎng)SQL語(yǔ)句時(shí)特別有用。
private static void sqlStatement() {
String sql = """
SELECT id, firstName, lastName\s\
FROM Employee
WHERE departmentId = "IT" \
ORDER BY lastName, firstName""";
System.out.println(text);
}
在Java 17中,對(duì)Switch語(yǔ)句進(jìn)行了進(jìn)一步的改進(jìn),可以解決忘記寫break關(guān)鍵字導(dǎo)致的bug問(wèn)題。
Switch表達(dá)式是一種更靈活的Switch語(yǔ)法形式,在每個(gè)case塊中可以返回一個(gè)值,并且可以在賦值等操作中使用這些返回值。它使用箭頭符號(hào)(->)代替冒號(hào)(:)來(lái)表示返回的表達(dá)式。
在此表達(dá)式中,當(dāng)使用switch返回時(shí),不需要使用break關(guān)鍵字來(lái)終止每個(gè)case塊,但是需要使用default case。
這種改進(jìn)使得Switch語(yǔ)句更加簡(jiǎn)潔和易讀,并且減少了由于忘記寫break而導(dǎo)致的錯(cuò)誤。
private static void improvedSwitch(Fruit fruit) {
String text = switch (fruit) {
case APPLE, PEAR -> {
System.out.println("the given fruit was: " + fruit);
yield "Common fruit";
}
case ORANGE, AVOCADO -> "Exotic fruit";
default -> "Undefined fruit";
};
System.out.println(text);
}如果在switch case中進(jìn)行多個(gè)操作,我們可以使用case塊,并使用yield關(guān)鍵字表示返回值。yield在這里是上下文相關(guān)的關(guān)鍵字,即在函數(shù)的其他地方可以有一個(gè)變量名yield。
Record類是一種特殊的不可變類,旨在替代數(shù)據(jù)傳輸對(duì)象(DTO)。
通常情況下,如果我們想在類或方法中使用一些POJO(普通Java對(duì)象),我們需要聲明該類,并定義所有的getters、setters、equals和hashCode函數(shù)。例如,在其他地方使用一個(gè)樣例 Fruit 類,我們需要用以下方式定義類:
public class Fruit {
private String name;
private int price;
//獲取器和設(shè)置器方法、equals 和 hashcode 方法
}盡管可以使用像 lombok 這樣的庫(kù)來(lái)減少大部分樣板代碼,但是如果借助 records,可以進(jìn)一步減少代碼量,無(wú)需依賴額外的庫(kù)。
使用 records 后,相同的代碼變?yōu)椋?/p>
public static void doSomething() {
record Fruit(String name, int price) {}
Fruit fruit = new Fruit("Apple", 100);
System.out.println(fruit.getPrice());
}如我們所見,使用Record類甚至可以在方法內(nèi)定義一個(gè)局部的record對(duì)象。這個(gè)record對(duì)象會(huì)自動(dòng)為其所有字段生成getter、setter、equals和hashCode方法。
Record中的字段是不可變的,只能通過(guò)在聲明record時(shí)提供的參數(shù)進(jìn)行定義。但我們可以定義靜態(tài)變量。我們還可以定義一個(gè)自定義構(gòu)造函數(shù)來(lái)驗(yàn)證字段。建議不要重寫record的getter和setter,否則會(huì)影響其不可變性。
下面是一個(gè)具有多個(gè)構(gòu)造函數(shù)、靜態(tài)變量和方法的 record 的示例:
public record Employee(int id, String firstName,
String lastName)
{
static int empToken;
// 緊湊構(gòu)造函數(shù)
public Employee
{
if (id < 100) {
throw new IllegalArgumentException(
"Employee Id cannot be below 100.");
}
if (firstName.length() < 2) {
throw new IllegalArgumentException(
"First name must be 2 characters or more.");
}
}
// 另一種構(gòu)造函數(shù)
public Employee(int id, String firstName)
{
this(id, firstName, null);
}
// 實(shí)例方法
public void getFullName()
{
if (lastName == null)
System.out.println(firstName());
else
System.out.println(firstName() + " "
+ lastName());
}
// 靜態(tài)方法
public static int generateEmployeeToken()
{
return ++empToken;
}
}record 類的更多特性還包括:
sealed 類為我們提供了更多對(duì)擴(kuò)展類的控制權(quán)。
在 Java 11 中,類可以是 final 或可擴(kuò)展的。如果您想控制哪些類可以擴(kuò)展您的超類,可以將所有類放在同一個(gè)包中,并將超類設(shè)為包可見性。然而,從包外部無(wú)法訪問(wèn)超類。例如,看下面的代碼:
public abstract class Fruit {
}
public final class Apple extends Fruit {
}
public final class Pear extends Fruit {
}
private static void problemSpace() {
Apple apple = new Apple();
Pear pear = new Pear();
Fruit fruit = apple;
class Avocado extends Fruit {};
}在這里,我們無(wú)法阻止 Avocado 擴(kuò)展 Fruit 類。如果我們將 Fruit 類設(shè)置為默認(rèn)訪問(wèn)級(jí)別,那么將無(wú)法把 apple 分配給 fruit 對(duì)象。
Java 17 引入sealed 類新特性,用于限制類的繼承層級(jí)。通過(guò)使用 sealed 關(guān)鍵字,可以控制哪些類可以擴(kuò)展或?qū)崿F(xiàn)一個(gè)特定的父類或接口,只允許特定的類擴(kuò)展我們的超類。以下是一個(gè)示例:
public abstract sealed class FruitSealed permits AppleSealed, PearSealed {
}
public non-sealed class AppleSealed extends FruitSealed {
}
public final class PearSealed extends FruitSealed {
}如我們所見,這里使用了一個(gè)新的關(guān)鍵字 sealed 來(lái)表示這是一個(gè) sealed 類。使用 permits 關(guān)鍵字定義可以擴(kuò)展的類。任何擴(kuò)展 sealed 類的類都可以是 final 類,如 PearSealed,或者可以在聲明類時(shí)使用 non-sealed 關(guān)鍵擴(kuò)展的其他類,比如 AppleSealed。
這種實(shí)現(xiàn)允許將 AppleSealed 分配給 FruitSealed 類,但不允許 permits 關(guān)鍵字未定義的其他類擴(kuò)展 FruitSealed 類。
在 Java 11 中,我們通常使用 instanceof 運(yùn)算符來(lái)檢查一個(gè)對(duì)象是否屬于某個(gè)類。如果我們希望在 instance of 檢查返回 true 后對(duì)其進(jìn)行某些操作,需要顯式將對(duì)象轉(zhuǎn)換為該特定類。以下是一個(gè)示例:
private static void oldStyle() {
Object o = new Grape(Color.BLUE, 2);
if (o instanceof GrapeClass) {
Grape grape = (Grape) o;
System.out.println("This grape has " + grape.getPits() + " pits.");
}
}在這里,我們需要顯式將對(duì)象轉(zhuǎn)換為 Grape 類型,然后找出核數(shù)。
使用 Java 17,可以將其更改為:
private static void patternMatchingInJava17() {
Object o = new Grape(Color.BLUE, 2);
if (o instanceof Grape grape) {
System.out.println("This grape has " + grape.getPits() + " pits.");
}
}可以將 instance of 檢查與 &&(and)條件配對(duì)使用,但不能使用 ||(or)條件。因?yàn)槿绻恰皁r”條件,即使 instance of 檢查返回 false,語(yǔ)句也可能達(dá)到另一個(gè)條件。
如果 instance of 檢查返回 true,變量 grape 的作用域甚至可以超出 if 塊。在下面的示例中,如果對(duì)象不是 Grape 類型,將拋出 RuntimeException 異常,因此編譯器在達(dá)到打印語(yǔ)句時(shí)會(huì)確保 grape 對(duì)象存在。
private static void patternMatchingScopeException() {
Object o = new Grape(Color.BLUE, 2);
if (!(o instanceof Grape grape)) {
throw new RuntimeException();
}
System.out.println("This grape has " + grape.getPits() + " pits.");
}
在Java 11中,當(dāng)遇到空指針異常時(shí),我們只能得到異常發(fā)生的行號(hào),但無(wú)法得知導(dǎo)致空指針異常的方法或變量。
在Java 17中,錯(cuò)誤消息得到了改進(jìn),空指針異常的消息會(huì)告訴我們導(dǎo)致空指針異常的具體方法調(diào)用。
public static void main(String[] args) {
HashMap grapes = new HashMap<>();
grapes.put("grape1", new GrapeClass(Color.BLUE, 2));
grapes.put("grape2", new GrapeClass(Color.WHITE, 4));
grapes.put("grape3", null);
var color = ((Grape) grapes.get("grape3")).getColor();
} 正如所看到的,這里試圖獲取一個(gè)為null的"grape3"對(duì)象的顏色。當(dāng)我們比較在Java 11和Java 17中獲得的錯(cuò)誤消息時(shí),我們可以看到錯(cuò)誤消息的差異,現(xiàn)在我們確切地知道在map中對(duì)null對(duì)象調(diào)用get方法導(dǎo)致了異常。
// Java 11
Exception in thread "main" java.lang.NullPointerException
at com.rg.java17.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)
// Java 17
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.rg.java17.Grape.getColor()" because the return value of "java.util.HashMap.get(Object)" is null
at com.rg.java17.HelpfulNullPointerExceptions.main(HelpfulNullPointerExceptions.java:13)
在NumberFormat類中添加了一個(gè)工廠方法,用于根據(jù)Unicode標(biāo)準(zhǔn)以緊湊、人類可讀的形式格式化數(shù)字。有SHORT和LONG兩種格式可用,示例如下:
NumberFormat shortFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT);
System.out.println(shortFormat.format(1000))
NumberFormat longFormat = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG);
System.out.println(shortFormat.format(1000))
// 輸出
1K
1 thousand
在DateTime模式中添加了一個(gè)新的模式"B",用于指定一天中的時(shí)間。
DateTimeFormatter timeOfDayFormatter = DateTimeFormatter.ofPattern("B");
System.out.println(timeOfDayFormatter.format(LocalTime.of(8, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(13, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(20, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(23, 0)));
System.out.println(timeOfDayFormatter.format(LocalTime.of(0, 0)));
// 輸出
早上
下午
晚上
夜間
午夜
在內(nèi)存使用和時(shí)間復(fù)雜度方面,Java 17相對(duì)于Java 11也有所改進(jìn)。其中進(jìn)行了一項(xiàng)基準(zhǔn)測(cè)試,對(duì)兩個(gè)版本的代碼進(jìn)行性能測(cè)試,讓它們執(zhí)行一系列任務(wù)。
一些總體結(jié)果如下:
從Java 11遷移到Java 17可帶來(lái)多項(xiàng)好處,包括新功能和性能改進(jìn)。然而,在遷移過(guò)程中需要注意潛在的問(wèn)題。許多庫(kù)也會(huì)升級(jí)到支持Java 17的新版本,因此在使用外部庫(kù)時(shí)需要謹(jǐn)慎。通過(guò)了解可能出現(xiàn)的問(wèn)題并采取必要的步驟解決,可以確保您順利地遷移到Java 17。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流