[每日短篇] 29 - Java 17 LTS 語言“新特性”

Java 17 在 2021 年 9 月 14 日正式發佈了,它是 LTS 版,標準提供 8 年支持到 2029 年 9 月。我對這次發佈之後技術社區的平靜感到意外,似乎沒有太多人關心這次發佈中的一件大事——Java Free License,Oracle 最終通過一個明確的許可證允許了 Oracle JDK 的免費商業和生產使用,過去切換成 OpenJDK 的公司裏估計有不少又要切換一次了😅。

Oracle 還提了一個提案要把 LTS 版的發佈週期從三年改爲兩年,如果通過的話,對於推進版本更新不無好處。

下面是正題,“新特性”,爲什麼要加引號呢,因爲實際上列出來的是 Java 12-17 中值得使用和知道的新特性,畢竟 Java 12 之後的變化對很多人來說也是新的(也有一些人對 Java 8 之後的都是新的,有沒有 Java 8 也是新的的我就不敢想了🤣)

1. 壓縮數字格式格式,將數字按指定區域的格式轉換成簡短的數字文本 (Java 12)

public static void compact() {
    System.out.println(
            NumberFormat.getCompactNumberInstance(
                    Locale.CHINA, NumberFormat.Style.SHORT).format(1_9200));
    System.out.println(
            NumberFormat.getCompactNumberInstance(
                    Locale.CHINA, NumberFormat.Style.LONG).format(1_9200));
    System.out.println(
            NumberFormat.getCompactNumberInstance(
                    Locale.ENGLISH, NumberFormat.Style.SHORT).format(19_200));
    System.out.println(
            NumberFormat.getCompactNumberInstance(
                    Locale.ENGLISH, NumberFormat.Style.LONG).format(19_200));
}

上面的代碼輸出爲

2萬
2萬
19K
19 thousand

2. 人類友好的 NPE 提示 (Java 14, JEP 358 Helpful NullPointerExceptions)

現在鏈式調用使用頻繁,空指針異常時一個很大的困擾是不知道具體是哪個對象是 null,在啓動程序時增加開關 -XX:+ShowCodeDetailsInExceptionMessages,當遇到空指針異常時異常輸出中會包括具體位置,輸出類似這樣

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.example.NullPointerExample$City.name()" because the return value of "com.example.NullPointerExample$Location.city()" is null
    at com.example.NullPointerExample.main(NullPointerExample.java:25)

會指明 city()null 導致 City.name() 調用產生空指針異常。

3. Switch Expressions (Java 14)

public int switchExpression2(TimeUnit tu) {
	return switch (tu) {
	case DAYS -> {
		...
		yield 1;
	}
	case HOURS, MINUTES, SECONDS -> 2;
	case MICROSECONDS, MILLISECONDS, NANOSECONDS -> 3;
	default -> 4;
	};
}

這裏有幾個大的改進

  • 多個值可以寫在一個 case 中
  • 不用寫 break
  • switch 是表達式,有值可以賦值給變量或者當作方法返回值

4. CMS 被移除 (Java 14)

這沒啥可解釋的,面試題好多公司的都沒更新,我也不好說忘掉它吧。

5. 文本塊 (Java 15)

原來寫

public void text() {
	String query = "SELECT \"EMP_ID\", \"LAST_NAME\" FROM \"EMPLOYEE_TB\"\n" +
			"WHERE \"CITY\" = 'INDIANAPOLIS'\n" +
			"ORDER BY \"EMP_ID\", \"LAST_NAME\";\n";
}

現在簡化爲

public void textBlock() {
	String query = """
			SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
			WHERE "CITY" = 'INDIANAPOLIS'
			ORDER BY "EMP_ID", "LAST_NAME";
			""";
}

經常要寫多行文本的可以充分利用文本塊的好處了,拼 SQL、JSON、YAML 可能是最常見的場景了。

6. JavaScript 引擎 Nashorn 被移除 (Java 15)

並且也沒有提供新的。移除的原因是 JavaScript 更新太快跟不上造成負擔了。使用內置 JavaScript 引擎的要麼別升級要麼找第三方庫,javax.script 還在。

7. JDK 源碼允許 C++ 14 特性 (Java 16)

經過謹慎評估在 HotSpot 中允許使用 C++ 14 的特性了,之前只到 03。這個對 Java 語法沒有影響,對 JDK 的開發者有影響,C++ 愛好者(跟 C++ 本身不是一回事兒)和技術保守主義者也可以各取所需。

8. JDK 源碼管理工具從 Mercurial 遷移到 Git (Java 16)

這對語法也沒影響,Git 愛好者喜歡。

9. JDK 源碼遷移到 GitHub (Java 16)

Git 愛好者也得知道 Linus 剛噴了 GitHub 的合併。

10. JDK 移植到 Alpine Linux (Java 16)

這麼說不太全面,關鍵點在於支持 musl(一個 C 標準庫實現,glibc 的同類),對容器化是個福音。

11. 對值類型類使用 synchronized 時產生警告 (Java 16)

Java 未來的優化中可能會修改編程模型,其中的值類型(目前比如原生類型的包裝類,現在還不能自定義)可以進行內聯等優化,所以對值類型使用 synchronized 未來會造成錯誤。現在如果錯誤使用會得到一條類似下面這樣的警告

Integer is a value-based type which is a discouraged argument for the synchronized statement

順便說一句,早在 Java 9 時原生類型包裝類的構造方法已經標記爲 @Deprecated 的了,正確的獲取方法爲使用 valueOf

12. 打包工具 (Java 16)

提供了命令行和 API 方式生成安裝包的能力,支持 deb、rpm、pkg、dmg、msi、exe 格式。

13. instanceof 的模式匹配 (Java 16)

這個特性簡單來說是過去這麼寫的代碼

if (obj instanceof String) {
    String s = (String) obj;
    ...
}

現在可以這麼寫了

if (obj instanceof String s) {
    ...
}

語法上細節比較多,要是仔細解釋夠單寫一篇了,比如說可以寫 obj instanceof String str && str.equals("a") 但是不能寫 obj instanceof String str || str.equals("a"),因爲 str 只有 obj instanceof String 成立時才存在。

14. record (Java 16)

這是繼 class、interface、@interface、enum 之後又一種類型,可以理解成不可變類型,比如

public record Point(int x, int y) { }

大致等價於

public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int x() {
        return x;
    }

    public int y() {
        return y;
    }

    public boolean equals(Object o) {
        if (!(o instanceof Point)) return false;
        Point other = (Point) o;
        return other.x == x && other.y == y;
    }

    public int hashCode() {
        return Objects.hash(x, y);
    }

    public String toString() {
        return String.format("Point[x=%d, y=%d]", x, y);
    }
}

其中 getter 的命名相當現代化,個人比較喜歡,對喜歡 getXxx、isXxx 的人來說有點難受。常變量是函數式編程以及解決併發性能和正確性的利器,有一部分程序員一定喜歡。

15. Stream.toList() (Java 16)

這個特性簡單又實用,以前寫 .collect(Collectors.toList()) 現在簡化成 .toList() 了。

16. 全面使用嚴格模式的浮點數 (Java 17)

不知道 strictfp 這個關鍵字的其實影響也不大,現在相當於所有的浮點數都帶 strictfp 了,知道這個關鍵字的以後就省心了。

17. Sealed Classes (Java 17)

這個是爲了解決既要使用類繼承體系又不希望外人隨便擴展的問題增加的特性,增加了 sealedpermitsnon-sealed 三個關鍵字,能夠形成全封閉或者部分開放的繼承體系。鑑於現在亂用繼承很普及的狀況,估計不會被普遍應用。

以上是 Java 12-17 這三年裏 Java 值得注意的事情,其實 Java Free License 足以擔當序號 0,不過懶得改了,就這樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章