大家好呀,我是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下將無法通過編譯.,到這時候再換就真的晚了… …