300 秒快速了解 Java 9 - 16 新特性,助你脫離內(nèi)卷

作者 | 空無
點贊再看,養(yǎng)成好習慣
JAVA 這幾年的更新實在是太太太……快了,JAVA 8 都還沒用多久,16 都已經(jīng)發(fā)布了。自從 JAVA 8 發(fā)布了 Lambda 和 Stream 之后,JAVA 就像打了雞血一樣,半年一個版本的發(fā)布,生產(chǎn)隊的驢也沒這么勤快。
導致我們現(xiàn)在完全跟不上 JAVA 發(fā)布的節(jié)奏,我司目前還停留在 JAVA 8,甚至部分老系統(tǒng)還在使用 JAVA 7,根本不能輕易的升級。
不過雖然暫時用不上最新版本的 JAVA,但了解每個新版本的主要特性還是很重要的,不然哪天真跟著升級了,那還不得一臉懵逼。
本文就帶你快速了解 JAVA 9 - 16 的主要新特性,早學完早下班!
JAVA 9(2017年9月)
接口里可以添加私有接口
JAVA 8 對接口增加了默認方法的支持,在 JAVA 9 中對該功能又來了一次升級,現(xiàn)在可以在接口里定義私有方法,然后在默認方法里調(diào)用接口的私有方法。
這樣一來,既可以重用私有方法里的代碼,又可以不公開代碼
public interface TestInterface {
default void wrapMethod(){
innerMethod();
}
private void innerMethod(){
System.out.println("");
}
}
匿名內(nèi)部類也支持鉆石(diamond)運算符
JAVA 5 就引入了泛型(generic),到了 JAVA 7 開始支持鉆石(diamond)運算符:<>,可以自動推斷泛型的類型:
List<Integer> numbers = new ArrayList<>();
但是這個自動推斷類型的鉆石運算符可不支持匿名內(nèi)部類,在 JAVA 9 中也對匿名內(nèi)部類做了支持:
List<Integer> numbers = new ArrayList<>() {
...
}
增強的 try-with-resources
JAVA 7 中增加了try-with-resources的支持,可以自動關閉資源:
try (BufferedReader bufferReader = new BufferedReader(...)) {
return bufferReader.readLine();
}
但需要聲明多個資源變量時,代碼看著就有點惡心了,需要在 try 中寫多個變量的創(chuàng)建過程:
try (BufferedReader bufferReader0 = new BufferedReader(...);
BufferedReader bufferReader1 = new BufferedReader(...)) {
return bufferReader0.readLine();
}
JAVA 9 中對這個功能進行了增強,可以引用 try 代碼塊之外的變量來自動關閉:
BufferedReader bufferReader0 = new BufferedReader(...);
BufferedReader bufferReader1 = new BufferedReader(...);
try (bufferReader0; bufferReader1) {
System.out.println(br1.readLine() + br2.readLine());
}
JAVA 10(2018年3月)
局部變量的自動類型推斷(var)
JAVA 10 帶來了一個很有意思的語法 - var,它可以自動推斷局部變量的類型,以后再也不用寫類型了,也不用靠 lombok 的 var注解增強了
var message = "Hello, Java 10";
不過這個只是語法糖,編譯后變量還是有類型的,使用時還是考慮下可維護性的問題,不然寫多了可就成 JavaScript 風格了
JAVA 11(2018年9月)
Lambda 中的自動類型推斷(var)
JAVA 11 中對 Lambda 語法也支持了 var 這個自動類型推斷的變量,通過 var 變量還可以增加額外的注解:
List<String> languages = Arrays.asList("Java", "Groovy");
String language = sampleList.stream()
.map((@Nonnull var x) -> x.toUpperCase())
.collect(Collectors.joining(", "));
assertThat(language).isEqualTo("Java, Groovy");
javac + java 命令一把梭
以前編譯一個 java 文件時,需要先 javac 編譯為 class,然后再用 java 執(zhí)行,現(xiàn)在可以一把梭了:
$ java HelloWorld.java
Hello Java 11!
Java Flight Recorder 登陸 OpenJDK
Java Flight Recorder 是個灰常好用的調(diào)試診斷工具,不過之前是在 Oracle JDK 中,也跟著 JDK 11 開源了,OpenJDK 這下也可以用這個功能,真香!
JAVA 12(2019年3月)
更簡潔的 switch 語法
在之前的 JAVA 版本中,switch語法還是比較啰嗦的,如果多個值走一個邏輯需要寫多個 case:
DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
String typeOfDay = "";
switch (dayOfWeek) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
typeOfDay = "Working Day";
break;
case SATURDAY:
case SUNDAY:
typeOfDay = "Day Off";
}
到了 JAVA 12,這個事情就變得很簡單了,幾行搞定,而且!還支持返回值:
typeOfDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";
case SATURDAY, SUNDAY -> "Day Off";
};
instanceof + 類型強轉(zhuǎn)一步到位
之前處理動態(tài)類型碰上要強轉(zhuǎn)時,需要先 instanceof 判斷一下,然后再強轉(zhuǎn)為該類型處理:
Object obj = "Hello Java 12!";
if (obj instanceof String) {
String s = (String) obj;
int length = s.length();
}
現(xiàn)在 instanceof 支持直接類型轉(zhuǎn)換了,不需要再來一次額外的強轉(zhuǎn):
Object obj = "Hello Java 12!";
if (obj instanceof String str) {
int length = str.length();
}
JAVA 13(2019年9月)
switch 語法再增強
JAVA 12 中雖然增強了 swtich 語法,但并不能在 -> 之后寫復雜的邏輯,JAVA 12 帶來了 swtich更完美的體驗,就像 lambda 一樣,可以寫邏輯,然后再返回:
typeOfDay = switch (dayOfWeek) {
case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
// do sth...
yield "Working Day";
}
case SATURDAY, SUNDAY -> "Day Off";
};
文本塊(Text Block)的支持
你是否還在為大段帶換行符的字符串報文所困擾,換行吧一堆換行符,不換行吧看著又難受:
String json = "{\"id\":\"1697301681936888\",\"nickname\":\"空無\",\"homepage\":\"https://juejin.cn/user/1697301681936888\"}";
JAVA 13 中幫你解決了這個惡心的問題,增加了文本塊的支持,現(xiàn)在可以開心的換行拼字符串了,就像用模板一樣:
String json = """
{
"id":"1697301681936888",
"nickname":"空無",
"homepage":"https://juejin.cn/user/1697301681936888"
}
""";
JAVA 14(2020年3月)
新增的 record 類型,干掉復雜的 POJO 類
一般我們創(chuàng)建一個 POJO 類,需要定義屬性列表,構造函數(shù),getter/setter,比較麻煩。JAVA 14 為我們帶來了一個便捷的創(chuàng)建類的方式 - record
public record UserDTO(String id,String nickname,String homepage) { };
public static void main( String[] args ){
UserDTO user = new UserDTO("1697301681936888","空無","https://juejin.cn/user/1697301681936888");
System.out.println(user.id);
System.out.println(user.nickname);
System.out.println(user.id);
}
IDEA 也早已支持了這個功能,創(chuàng)建類的時候直接就可以選:
不過這個只是一個語法糖,編譯后還是一個 Class,和普通的 Class 區(qū)別不大
更直觀的 NullPointerException 提示
NullPointerException 算是 JAVA 里最常見的一個異常了,但這玩意提示實在不友好,遇到一些長一點的鏈式表達式時,沒辦法分辨到底是哪個對象為空。
比如下面這個例子中,到底是 innerMap 為空呢,還是 effected 為空呢?
Map<String,Map<String,Boolean>> wrapMap = new HashMap<>();
wrapMap.put("innerMap",new HashMap<>());
boolean effected = wrapMap.get("innerMap").get("effected");
// StackTrace:
Exception in thread "main" java.lang.NullPointerException
at org.example.App.main(App.java:50)
JAVA 14 也 get 到了 JAVAER 們的痛點,優(yōu)化了 NullPointerException 的提示,讓你不在困惑,一眼就能定位到底“空”在哪!
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.lang.Boolean.booleanValue()" because the return value of "java.util.Map.get(Object)" is null
at org.example.App.main(App.java:50)
現(xiàn)在的 StackTrace 就很直觀了,直接告訴你 effected 變量為空,再也不用困惑!
安全的堆外內(nèi)存讀寫接口,別再玩 Unsafe 的騷操作了
在之前的版本中,JAVA 如果想操作堆外內(nèi)存(DirectBuffer),還得 Unsafe 各種 copy/get/offset。現(xiàn)在直接增加了一套安全的堆外內(nèi)存訪問接口,可以輕松的訪問堆外內(nèi)存,再也不用搞 Unsafe 的騷操作了。
// 分配 200B 堆外內(nèi)存
MemorySegment memorySegment = MemorySegment.allocateNative(200);
// 用 ByteBuffer 分配,然后包裝為 MemorySegment
MemorySegment memorySegment = MemorySegment.ofByteBuffer(ByteBuffer.allocateDirect(200));
// MMAP 當然也可以
MemorySegment memorySegment = MemorySegment.mapFromPath(
Path.of("/tmp/memory.txt"), 200, FileChannel.MapMode.READ_WRITE);
// 獲取堆外內(nèi)存地址
MemoryAddress address = MemorySegment.allocateNative(100).baseAddress();
// 組合拳,堆外分配,堆外賦值
long value = 10;
MemoryAddress memoryAddress = MemorySegment.allocateNative(8).baseAddress();
// 獲取句柄
VarHandle varHandle = MemoryHandles.varHandle(long.class, ByteOrder.nativeOrder());
varHandle.set(memoryAddress, value);
// 釋放就這么簡單,想想 DirectByteBuffer 的釋放……多奇怪
memorySegment.close();
不了解 Unsafe 操作堆外內(nèi)存方式的同學,可以參考我的另一篇文章《JDK中為了性能大量使用的Unsafe類,你會用嗎?》
新增的 jpackage 打包工具,直接打包二進制程序,再也不用裝 JRE 了
之前如果想構建一個可執(zhí)行的程序,還需要借助三方工具,將 JRE 一起打包,或者讓客戶電腦也裝一個 JRE 才可以運行我們的 JAVA 程序。
現(xiàn)在 JAVA 直接內(nèi)置了 jpackage 打包工具,幫助你一鍵打包二進制程序包,終于不用亂折騰了
JAVA 15(2020年9月)
ZGC 和 Shenandoah 兩款垃圾回收器正式登陸
在 JAVA 15中,ZGC 和 Shenandoah 再也不是實驗功能,正式登陸了(不過 G1 仍然是默認的)。如果你升級到 JAVA 15 以后的版本,就趕快試試吧,性能更強,延遲更低
封閉(Sealed )類
JAVA 的繼承目前只能選擇允許繼承和不允許繼承(final 修飾),現(xiàn)在新增了一個封閉(Sealed )類的特性,可以指定某些類才可以繼承:
public sealed interface Service permits Car, Truck {
int getMaxServiceIntervalInMonths();
default int getMaxDistanceBetweenServicesInKilometers() {
return 100000;
}
}
JAVA 16(2021年3月)
JAVA 16 在用戶可見的地方 變化并不多,基本都是 14/15 的實驗性內(nèi)容,到了 16 正式發(fā)布,這里就不重復介紹了。
總結
以上介紹的各種新特性,有些特性在歷史版本中還屬于實驗性功能,不過按照 JAVA 目前這個驢一樣的更新頻率,很可能下個版本就是穩(wěn)定版了。早學早享受,晚學被卷走……
看看時間,300 秒到了嗎?
- END -
往期推薦
關注我回復「加群」,加入Spring技術交流群


