我有點想用JDK17了

sum墨發表於2024-06-04

大家好呀,我是summo,JDK版本升級的非常快,現在已經到JDK20了。JDK版本雖多,但應用最廣泛的還得是JDK8,正所謂“他發任他發,我用Java8”。

其實我也不太想升級JDK版本,感覺投入高,收益小,不過有一次我看到了一些使用JDK17新語法寫的程式碼,讓我改變了對升級JDK的看法,因為這些新語法我確實想用!

廢話不多說,上程式碼!

一、JDK17語法新特性

1. 文字塊

這個更新非常實用。在沒有這個特性之前,編寫長文字非常痛苦。雖然IDEA等整合開發工具可以自動處理,但最終效果仍然醜陋,充滿拼接符號。現在,透過字串塊,我們可以輕鬆編寫JSON、HTML、SQL等內容,效果更清爽。這個新特性值得五顆星評價,因為它讓我們只需關注字串本身,而無需關心拼接操作。

原來的寫法

/**
 * 使用JDK8返回HTML文字
 *
 * @return 返回HTML文字
 */
public static final String getHtmlJDK8() {
    return "<html>\n" +
        " <body>\n" +
        " <p>Hello, world</p>\n" +
        " </body>\n" +
        "</html>";
}

新的寫法

/**
 * 使用JDK17返回HTML文字
 *
 * @return 返回HTML文字
 */
public static final String getHtmlJDK17() {
    return """
        <html>
            <body>
                <p>Hello, world</p>
            </body>
        </html>
        """;
}

推薦指數:⭐️⭐️⭐️⭐️⭐️

2. NullPointerException增強

這一功能非常強大且實用,相信每位Java開發者都期待已久。空指標異常(NPE)一直是Java程式設計師的痛點,因為報錯資訊無法直觀地指出哪個物件為空,只丟擲一個NullPointerException和一堆堆疊資訊,定位問題耗時且麻煩。尤其在遇到喜歡級聯呼叫的程式碼時,逐行排查更是令人頭疼。如果在測試環境中,可能還需透過遠端除錯查明空物件,費時費力。為此,阿里的編碼規範甚至不允許級聯呼叫,但這並不能徹底解決問題。Java17終於在這方面取得了突破,提供了更詳細的空指標異常資訊,幫助開發者迅速定位問題源頭。

public static void main(String[] args) {
    try {
        //簡單的空指標
        String str = null;
        str.length();
    } catch (Exception e) {
        e.printStackTrace();
    }
    try {
        //複雜一點的空指標
        var arr = List.of(null);
        String str = (String)arr.get(0);
        str.length();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

執行結果

推薦指數:⭐️⭐️⭐️⭐️⭐️

3. Records

在Java中,POJO物件(如DO、PO、VO、DTO等)通常包含成員變數及相應的Getter和Setter方法。儘管可以透過工具或IDE生成這些程式碼,但修改和維護仍然麻煩。Lombok外掛為此出現,能夠在編譯期間自動生成Getter、Setter、hashcode、equals和建構函式等程式碼,使用起來方便,但對團隊有依賴要求。
為此,Java引入了標準解決方案:Records。它透過簡潔的語法定義資料類,大大簡化了POJO類的編寫,如下所示。雖然hashcode和equals方法仍需手動編寫,但IDE能夠自動生成。這一特性有效解決了模板程式碼問題,提升了程式碼整潔度和可維護性。

package com.summo.jdk17;

/**
 * 3星
 *
 * @param stuId     學生ID
 * @param stuName   學生名稱
 * @param stuAge    學生年齡
 * @param stuGender 學生性別
 * @param stuEmail  學生郵箱
 */
public record StudentRecord(Long stuId,
                            String stuName,
                            int stuAge,
                            String stuGender,
                            String stuEmail) {
    public StudentRecord {
        System.out.println("建構函式");
    }

    public static void main(String[] args) {
        StudentRecord record = new StudentRecord(1L, "張三", 16, "男", "xxx@qq.com");
        System.out.println(record);
    }
}

推薦指數:⭐️⭐️⭐️⭐️

4. 全新的switch表示式

有人可能問了,Java語言不早已支援switch了嘛,有什麼好提的?講真,這次的提升還真有必要好好地來聊一聊了。在Java12的時候就引入了switch表示式,注意這裡是表示式,而不是語句,原來的switch是語句。如果不清楚兩者的區別的話,最好先去了解一下。主要的差別就是就是表示式有返回值,而語句則沒有。再配合模式匹配,以及yield和“->”符號的加入,全新的switch用起來爽到飛起來。

package com.summo.jdk17;

public class SwitchDemo {
    /**
     * 在JDK8中獲取switch返回值方式
     *
     * @param week
     * @return
     */
    public int getByJDK8(Week week) {
        int i = 0;
        switch (week) {
            case MONDAY, TUESDAY:
                i = 1;
                break;
            case WEDNESDAY:
                i = 3;
                break;
            case THURSDAY:
                i = 4;
                break;
            case FRIDAY:
                i = 5;
                break;
            case SATURDAY:
                i = 6;
                break;
            case SUNDAY:
                i = 7;
                break;
            default:
                i = 0;
                break;
        }

        return i;
    }

    /**
     * 在JDK17中獲取switch返回值
     *
     * @param week
     * @return
     */
    public int getByJDK17(Week week) {
        // 1, 現在的switch變成了表示式,可以返回值了,而且支援yield和->符號來返回值
        // 2, 再也不用擔心漏寫了break,而導致出問題了
        // 3, case後面支援寫多個條件
        return switch (week) {
            case null -> -1;
            case MONDAY -> 1;
            case TUESDAY -> 2;
            case WEDNESDAY -> 3;
            case THURSDAY -> {yield 4;}
            case FRIDAY -> 5;
            case SATURDAY, SUNDAY -> 6;
            default -> 0;
        };
    }

    private enum Week {
        MONDAY,
        TUESDAY,
        WEDNESDAY,
        THURSDAY,
        FRIDAY,
        SATURDAY,
        SUNDAY
    }
}

推薦指數:⭐️⭐️⭐️⭐️

5. 私有介面方法

從Java8開始,允許在interface裡面新增預設方法,其實當時就有些小困惑,如果一個default方法體很大怎麼辦,拆到另外的類去寫嗎?實在有些不太合理,所以在Java17裡面,如果一個default方法體很大,那麼可以透過新增介面私有方法來進行一個合理的拆分了,為這個小改進點個贊。

public interface PrivateInterfaceMethod {
    /**
     * 介面預設方法
     */
    default void defaultMethod() {
        privateMethod();
    }

    // 介面私有方法,在Java8裡面是不被允許的,不信你試試
    private void privateMethod() {
    }
}

推薦指數:⭐️⭐️⭐️

6. 模式匹配

在JDK 17中,模式匹配主要用於instanceof表示式。模式匹配增強了instanceof的語法和功能,使型別檢查和型別轉換更加簡潔和高效。在傳統的Java版本中,我們通常使用instanceof結合型別轉換來判斷物件型別並進行處理,這往往會導致冗長的程式碼。

原來的寫法

/**
 * 舊式寫法
 *
 * @param value
 */
public void matchByJDK8(Object value) {
    if (value instanceof String) {
        String v = (String)value;
        System.out.println("遇到一個String型別" + v.toUpperCase());
    } else if (value instanceof Integer) {
        Integer v = (Integer)value;
    System.out.println("遇到一個整型型別" + v.longValue());
    }
}

新的寫法

/**
 * 轉換並申請了一個新的變數,極大地方便了程式碼的編寫
 *
 * @param value
 */
public void matchByJDK17(Object value) {
    if (value instanceof String v) {
        System.out.println("遇到一個String型別" + v.toUpperCase());
    } else if (value instanceof Integer v) {
        System.out.println("遇到一個整型型別" + v.longValue());
    }
}

推薦指數:⭐️⭐️⭐️⭐️

7. 集合類的工廠方法

在Java8的年代,即便建立一個很小的集合,或者固定元素的集合都是比較麻煩的,為了簡潔一些,有時我甚至會引入一些依賴。

原來的寫法

Set<String> set = new HashSet<>();
set.add("a");
set.add("b");
set.add("c"

新的寫法

Set<String> set = Set.of("a", "b", "c");

推薦指數:⭐️⭐️⭐️⭐️⭐️

二、其他的新特性

1. 新的String方法

  • repeat:重複生成字串
  • isBlank:不用在引入第三方庫就可以實現字串判空了
  • strip:去除字串兩邊的空格,支援全形和半形,之前的trim只支援半形
  • lines:能根據一段字串中的終止符提取出行為單位的流
  • indent:給字串做縮排,接受一個int型的輸入
  • transform:接受一個轉換函式,實現字串的轉換

2. Stream API的增強

增加takeWhile, dropWhile, ofNullable, iterate以及toList的API,越來越像一些函式式語言了。用法舉例如下。

// takeWhile 順序返回符合條件的值,直到條件不符合時即終止繼續判斷,
// 此外toList方法的加入,也大大減少了節省了程式碼量,免去了呼叫collect(Collectors::toList)方法了
List<Integer> list = Stream.of(2,2,3,4,5,6,7,8,9,10)
        .takeWhile(i->(i%2==0)).toList(); // 返回2, 2

// dropWhile 順序去掉符合條件的值,直到條件不符合時即終止繼續判斷
List<Integer> list1 = Stream.of(2,2,3,4,5,6,7,8,9,10)
        .dropWhile(i->(i%2==0)).toList(); //返回3, 4, 5, 6, 7, 8, 9, 10

// ofNullable,支援傳入空流,若沒有這個且傳入一個空流,那麼將會拋NPE
var nullStreamCount = Stream.ofNullable(null).count(); //返回0

// 以下兩行都將輸出0到9
Stream.iterate(0, n -> n < 10, n -> n + 1).forEach(x -> System.out.println(x));
Stream.iterate(0, n -> n + 1).limit(10).forEach(x -> System.out.println(x));

3. 全新的HttpClient

這個API首次出現在9之中,不過當時並非是一個穩定版本,在Java11中正式得到釋出,所以在Java17裡面可以放心地進行使用。原來的JDK自帶的Http客戶端真的非常難用,這也就給了很多像okhttp、restTemplate、Apache的HttpClient和feign這樣的第三方庫極大的發揮空間,幾乎就沒有人願意去用原生的Http客戶端的。但現在不一樣了,感覺像是新時代的API了。FluentAPI風格,處處充滿了現代風格,用起來也非常地方便,再也不用去依賴第三方的包了,就兩個字,清爽。

// 同步請求
HttpClient client = HttpClient.newBuilder()
        .version(Version.HTTP_1_1)
        .followRedirects(Redirect.NORMAL)
        .connectTimeout(Duration.ofSeconds(20))
        .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
        .authenticator(Authenticator.getDefault())
        .build();
   HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
   System.out.println(response.statusCode());
   System.out.println(response.body()); 
// 非同步請求
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://foo.com/"))
        .timeout(Duration.ofMinutes(2))
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofFile(Paths.get("file.json")))
        .build();
   client.sendAsync(request, BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);
 

4. jshell

在新的JDK版本中,支援直接在命令列下執行java程式,類似於python的互動式REPL。簡而言之,使用 JShell,你可以輸入程式碼片段並馬上看到執行結果,然後就可以根據需要作出調整,這樣在驗證一些簡單的程式碼的時候,就可以透過jshell得到快速地驗證,非常方便。

5. java命令直接執行java檔案

在現在可以直接透過執行“java xxx.java”,即可執行該java檔案,無須先執行javac,然後再執行java,是不是又簡單了一步。

6. ZGC

在ParallelOldGC、CMS和G1之後,JDK 11引入了全新的ZGC(Z Garbage Collector)。這個名字本身就顯得很牛。官方宣稱ZGC的垃圾回收停頓時間不超過10ms,能支援高達16TB的堆空間,並且停頓時間不會隨著堆的增大而增加。那麼,ZGC到底解決了什麼問題?Oracle官方介紹它是一個可伸縮的低延遲垃圾回收器,旨在降低停頓時間,儘管這可能會導致吞吐量的降低。不過,透過橫向擴充套件伺服器可以解決吞吐量問題。官方已建議ZGC可用於生產環境,這無疑將成為未來的主流垃圾回收器。要了解更多,請參閱官方文件

三、小結一下

作為程式設計師,持續學習和充電非常重要。隨著Java8即將停止免費官方支援,越來越多的專案將轉向Java17,包括大名鼎鼎的Spring Boot 3.0,它在2022年1月20日釋出的第一個里程碑版本(M1)正是基於Java17構建的。該專案依賴的所有元件也將快速升級,未來如果想利用某些新特性,在Java8下將無法透過編譯.,到這時候再換就真的晚了... ...

相關文章