300 秒快速瞭解 Java 9 - 16 新特性

點贊再看,養成好習慣java

JAVA 這幾年的更新實在是太太太……快了,JAVA 8 都還沒用多久,16 都已經發布了。自從 JAVA 8 發佈了 Lambda 和 Stream 以後,JAVA 就像打了雞血同樣,半年一個版本的發佈,生產隊的驢也沒這麼勤快。 ​json

image (1).png

致使咱們如今徹底跟不上 JAVA 發佈的節奏,我司目前還停留在 JAVA 8,甚至部分老系統還在使用 JAVA 7,根本不能輕易的升級。 ​安全

不過雖然暫時用不上最新版本的 JAVA,但瞭解每一個新版本的主要特性仍是很重要的,否則哪天真跟着升級了,那還不得一臉懵逼。 ​bash

本文就帶你快速瞭解 JAVA 9 - 16 的主要新特性,早學完早下班!markdown

JAVA 9(2017年9月)

接口裏能夠添加私有接口

JAVA 8 對接口增長了默認方法的支持,在 JAVA 9 中對該功能又來了一次升級,如今能夠在接口裏定義私有方法,而後在默認方法裏調用接口的私有方法。 ​app

這樣一來,既能夠重用私有方法裏的代碼,又能夠不公開代碼ide

public interface TestInterface {
    default void wrapMethod(){
        innerMethod();
    }
    private void innerMethod(){
        System.out.println("");
    }
}
複製代碼

匿名內部類也支持鑽石(diamond)運算符

JAVA 5 就引入了泛型(generic),到了 JAVA 7 開始支持鑽石(diamond)運算符:<>,能夠自動推斷泛型的類型:函數

List<Integer> numbers = new ArrayList<>();
複製代碼

可是這個自動推斷類型的鑽石運算符可不支持匿名內部類,在 JAVA 9 中也對匿名內部類作了支持:工具

List<Integer> numbers = new ArrayList<>() {
    ...
}
複製代碼

加強的 try-with-resources

JAVA 7 中增長了try-with-resources的支持,能夠自動關閉資源:post

try (BufferedReader bufferReader = new BufferedReader(...)) {
    return bufferReader.readLine();
}
複製代碼

但須要聲明多個資源變量時,代碼看着就有點噁心了,須要在 try 中寫多個變量的建立過程:

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 執行,如今能夠一把梭了:

$ java HelloWorld.java
Hello Java 11!
複製代碼

Java Flight Recorder 登錄 OpenJDK

Java Flight Recorder 是個灰常好用的調試診斷工具,不過以前是在 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 + 類型強轉一步到位

以前處理動態類型碰上要強轉時,須要先 instanceof 判斷一下,而後再強轉爲該類型處理:

Object obj = "Hello Java 12!";
if (obj instanceof String) {
    String s = (String) obj;
    int length = s.length();
}
複製代碼

如今 instanceof 支持直接類型轉換了,不須要再來一次額外的強轉:

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 中幫你解決了這個噁心的問題,增長了文本塊的支持,如今能夠開心的換行拼字符串了,就像用模板同樣:

String json = """ { "id":"1697301681936888", "nickname":"空無", "homepage":"https://juejin.cn/user/1697301681936888"
				}
				"""; 複製代碼

JAVA 14(2020年3月)

新增的 record 類型,幹掉複雜的 POJO 類

通常咱們建立一個 POJO 類,須要定義屬性列表,構造函數,getter/setter,比較麻煩。JAVA 14 爲咱們帶來了一個便捷的建立類的方式 - 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 也早已支持了這個功能,建立類的時候直接就能夠選: image (2) (1).png 不過這個只是一個語法糖,編譯後仍是一個 Class,和普通的 Class 區別不大

更直觀的 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 們的痛點,優化了 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)
複製代碼

如今的 StackTrace 就很直觀了,直接告訴你 effected 變量爲空,不再用困惑!

安全的堆外內存讀寫接口,別再玩 Unsafe 的騷操做了

在以前的版本中,JAVA 若是想操做堆外內存(DirectBuffer),還得 Unsafe 各類 copy/get/offset。如今直接增長了一套安全的堆外內存訪問接口,能夠輕鬆的訪問堆外內存,不再用搞 Unsafe 的騷操做了。

// 分配 200B 堆外內存
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);

// 獲取堆外內存地址
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 操做堆外內存方式的同窗,能夠參考個人另外一篇文章《JDK中爲了性能大量使用的Unsafe類,你會用嗎?

新增的 jpackage 打包工具,直接打包二進制程序,不再用裝 JRE 了

以前若是想構建一個可執行的程序,還須要藉助三方工具,將 JRE 一塊兒打包,或者讓客戶電腦也裝一個 JRE 才能夠運行咱們的 JAVA 程序。 ​

如今 JAVA 直接內置了 jpackage 打包工具,幫助你一鍵打包二進制程序包,終於不用亂折騰了

JAVA 15(2020年9月)

ZGC 和 Shenandoah 兩款垃圾回收器正式登錄

在 JAVA 15中,ZGC 和 Shenandoah 不再是實驗功能,正式登錄了(不過 G1 仍然是默認的)。若是你升級到 JAVA 15 之後的版本,就趕快試試吧,性能更強,延遲更低

封閉(Sealed )類

JAVA 的繼承目前只能選擇容許繼承和不容許繼承(final 修飾),如今新增了一個封閉(Sealed )類的特性,能夠指定某些類才能夠繼承:

public sealed interface Service permits Car, Truck {

    int getMaxServiceIntervalInMonths();

    default int getMaxDistanceBetweenServicesInKilometers() {
        return 100000;
    }

}
複製代碼

JAVA 16(2021年3月)

JAVA 16 在用戶可見的地方變化並很少,基本都是 14/15 的實驗性內容,到了 16 正式發佈,這裏就不重複介紹了。 ​

總結

以上介紹的各類新特性,有些特性在歷史版本中還屬於實驗性功能,不過按照 JAVA 目前這個驢同樣的更新頻率,極可能下個版本就是穩定版了。早學早享受,晚學被捲走……

看看時間,300 秒到了嗎?

原創不易,禁止未受權的轉載。若是個人文章對您有幫助,就請點贊/收藏/關注鼓勵支持一下吧❤❤❤❤❤❤

相關文章
相關標籤/搜索