Java9-17新特性一覽,瞭解少於3個你可能脫節了

2022-12-04 15:01:31

前言

Java8出來這麼多年後,已經成為企業最成熟穩定的版本,相信絕大部分公司用的還是這個版本,但是一眨眼今年Java19都出來了,相信很多Java工程師忙於學習工作對新特性沒什麼瞭解,有的話也僅限於某一塊。


本篇就是博主對自己感覺有用的新特性做了一個案例驗證及簡要說明,整合起來分享給大家。


特別說明:Java17是繼Java8之後的一個重要里程碑,像SpringBoot3.0、IDEA2022、Jenkins新版、Kafka3.0等等很多生態都強制繫結支援這個版本,等於是給它背書,博主建議大家有必要花時間去了解一下。


如果沒時間看,可以先收藏一下,閒下來一邊喝茶一邊品讀。


你的收穫

首先,你能通過一篇簡單、連續、直觀的文章就明白Java8之後Java未來整體發展的趨勢,為之後幾年適應Java相關工作打下基礎;


其次,你可以通過了解Java9-17的新特性,為以後的面試加分,畢竟一個愛學習有態度的程式設計師會更受企業青睞;


最後,你可以看看博主對Java未來發展趨勢的粗淺看法,也許能給迷茫的你帶來收穫。


準備工作

首先,你要安裝Java17版本,環境變數設定還是和以前沒區別,這是我的版本。

其次,建議安裝IDEA2022.3,新版IDEA佔用記憶體比以前少很多,而且有一些增強支援。

安裝好後,需要做一個對Java17的設定,看圖。

setting設定

Project Structure設定

這裡特別說明一下,最好選擇預覽版本,因為Java17包含一些預覽功能,這裡不選預覽版本會編譯報錯。


新特性

一共分為了8個,按照版本順序來講述的,最後一個是因為幾個版本連續有增強,所以單獨拿出來。

1、介面private

1)、說明

Java9新特性,在介面中宣告private方法,不會被外部實現。

2)、案例

宣告一個介面,一個default方法,兩個private方法。

/**
 * <p>
 * 使用者資訊介面
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 15:03
 */
public interface UserInterface {
   private void getUsername() {
      System.err.println("你好,我是王小飛!");
   }

   private void getPassword() {
      System.err.println("你好,我是徐西圓!");
   }

   default void getData() {
      getUsername();
      getPassword();
   }
}

實現這個介面,可以看到只能實現default方法,無法實現private方法。

/**
 * <p>
 * JDK9新特性:介面private方法
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 15:10
 */
public class UserImpl implements UserInterface {

   @Override
   public void getData() {
      UserInterface.super.getData();
   }

   public static void main(String[] args) {
      UserImpl user = new UserImpl();
      user.getData();
   }
}

Ctrl+Insert可以檢視要實現的方法

3)、注意

private介面自動是default的,所以要有方法體,否則編譯不通過。


2、型別推斷

1)、說明

Java11新特性,在方法內部用var關鍵字宣告一個屬性或物件,可以被編譯器自動判斷型別。

2)、案例

案例包含兩個測試,一個是直接測試var宣告的變數是否能自己推斷型別,一個是在迴圈中使用的效果。

/**
 * <p>
 * JDK11新特性:型別推斷
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 11:52
 */
public class TypeInferenceDemo {

   public static void main(String[] args) {
      TypeInferenceDemo demo = new TypeInferenceDemo();
      // 測試型別推斷
//    demo.testVar();

      // 迴圈中使用
      List<UserInfo> userList = new ArrayList<>();
      UserInfo userInfo = new UserInfo();
      userInfo.setUsername("張三");
      userInfo.setPassword("123456");
      userList.add(userInfo);
      for (var user : userList) {
         System.err.println(user.getUsername() + " | " + user.getPassword());
      }
   }

   public void testVar() {
      var name = "張三";
      var age = 33;
      var id = 1001L;
      var amount = BigDecimal.ZERO;
      var user = new UserInfo();
      System.err.println("-------------------------------------------------");
      System.err.println(name);
      System.err.println(age);
      System.err.println(id);
      System.err.println(amount);
      System.err.println(user);
   }

   public static class UserInfo {
      private String username;
      private String password;

      public String getUsername() {
         return username;
      }

      public void setUsername(String username) {
         this.username = username;
      }

      public String getPassword() {
         return password;
      }

      public void setPassword(String password) {
         this.password = password;
      }
   }
}

測試的效果如圖

3)、注意

1)、只能在方法內部使用(區域性變數);

2)、必須有初始化值且不能為null;

3)、不能定義為方法的返回型別。


3、空指標優化

1)、說明

Java15新特性,就是把NullPointerException異常的紀錄檔做了優化列印的更人性化。

2)、案例

可以看到,提示會更有指向性,意味著以後在複雜的生產環境排錯過程中,你很可能不會再被空指標異常所困擾。

3)、注意

沒什麼可注意的


4、文字塊

1)、說明

JDK15新特性,就是替代了以前String中一堆換行符和雙引號的簡潔版寫法,相信你很難不喜歡。

2)、案例

可以看到,就是三引號取代了雙引號,因為雙引號的內容會夾帶一堆換行符,而三引號裡面就單純是內容,很清晰,而且雙引號換行需要槓n,而三引號直接換行即可生效。

/**
 * <p>
 * JDK15新特性:文字塊
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 23:36
 */
public class TextBlockDemo {
   private static final String str =
         "本以為大S和汪小菲的事情就這樣告一段落,彼此都過著屬於自己的幸福小日子,但是,被罵9天后,汪小菲口中的「窩囊廢」,終於有所行動了!\n"
         + "\n"
         + "也許具俊曄再也忍受不了別人罵自己窩囊廢,更不願意住在別人房子內,所以11月30日,據媒體爆料,具俊曄這次要男人一把,決定買一套屬於自己的房子,"
         + "屬於自己的床墊,搬出汪小菲買的豪宅,來證明自己的實力。";

   private static final String newStr = """
            本以為大S和汪小菲的事情就這樣告一段落,彼此都過著屬於自己的幸福小日子,但是,被罵9天后,汪小菲口中的「窩囊廢」,終於有所行動了!
            
            也許具俊曄再也忍受不了別人罵自己窩囊廢,更不願意住在別人房子內,所以11月30日,據媒體爆料,具俊曄這次要男人一把,決定買一套屬於自己的房子,
            屬於自己的床墊,搬出汪小菲買的豪宅,來證明自己的實力。
         """;

   public static void main(String[] args) {
      System.err.println(str);
      System.err.println("------------------------------------------------");
      System.err.println(newStr);
   }
}
3)、注意

文字塊只有一個要注意的地方,就是預設三引號內的內容沒有縮排,想要縮排的話要以末尾三引號為參照物向後偏移來確定縮排量。


下面一張圖會直接展示給你效果:


5、智慧轉型

1)、說明

Java16新特性,就是幫你對instanceof做了增強,智慧轉換變數型別。

2)、案例

可以對比old和new兩種寫法,第二種就是將第一種簡化了,型別轉換+宣告變數+後續邏輯判斷一起搞定。

/**
 * <p>
 * JDK16新特性:智慧轉型
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/3 14:02
 */
public class InstanceofMatching {
   /**
    * 舊寫法
    * @param obj 未知物件
    */
   static void testOld(Object obj) {
      if (obj instanceof String) {
         String s = (String) obj;
         if ("模式匹配".equals(s)) {
            System.err.println(s);
         }
      }
   }

   /**
    * 新寫法
    * @param obj 未知物件
    */
   static void testNew(Object obj) {
      if (obj instanceof String s && "模式匹配".equals(s)) {
         System.err.println(s);
      }
   }

   public static void main(String[] args) {
      testOld("Hello, Java!");
      testNew("模式匹配");
   }
}
3)、注意

instanceof後面若需要增強判斷必須要用&&,不能用||,因為instanceof本來就是做型別匹配的,Java可以確保&&後面的變數一定存在,但無法判斷||後面的變數一定存在,所以會報錯。


6、record類

1)、說明

Java16新特性,簡單講,就是有了它,宣告一個final類變得更簡潔和可讀。

2)、案例

先來看下寫法

/**
 * <p>
 * JDK16新特性:record類
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 21:52
 */
public record RecordDemo(int type, String typeName) {
   private void test() {
      System.err.println(type + " | " + typeName);
   }
   public static void main(String[] args) {
      // 這裡new的時候帶的引數其實就是類的屬性,等於宣告屬性+存取構造方法二合一。
      RecordDemo recordDemo = new RecordDemo(100, "葡萄牙");
      recordDemo.test();
   }
}

上面的新寫法等同於下面這種老的寫法,一看就懂。

/**
 * <p>
 * RecordDemo的寫法等同於這個類
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 21:55
 */
public final class RecordCustomDemo {
   final int type;
   final String typeName;

   public int type() {
      return type;
   }

   public String name() {
      return typeName;
   }

   public RecordCustomDemo(int type, String typeName) {
      this.type = type;
      this.typeName = typeName;
   }

   @Override
   public boolean equals(Object o) {
      if (this == o)
         return true;
      if (o == null || getClass() != o.getClass())
         return false;
      RecordCustomDemo that = (RecordCustomDemo) o;
      return type == that.type && Objects.equals(typeName, that.typeName);
   }

   @Override
   public int hashCode() {
      return Objects.hash(type, typeName);
   }
}
3)、注意

沒什麼可注意的,就跟使用正常的類差不多,只是自動生成了以下內容:


1)、括號裡的引數就是該類的屬性,且是final型別;

2)、自動生成一個帶有該屬性的構造器;

3)、自動生成該屬性的存取器,如xx.type()、xx.typeName();

4)、生成了equals和hashCode方法。


如果還是不懂,就理解成lombok中的@Data註解即可,同樣的意思。


7、密封類和介面

1)、說明

Java17新特性,密封類和密封介面。

使用sealed關鍵字宣告的類,可以通過設定permits關鍵字來控制哪些子類可以繼承它。

使用sealed關鍵字宣告的介面,可以通過設定permits關鍵字來控制哪些類可以實現它。。

簡單來講,就是爸爸規定哪個兒子能繼承財產。

2)、案例

看下密封類的寫法

用sealed宣告一個類,設定permits授權哪幾個子類可以繼承它。

/**
 * <p>
 * JDK17新特性:密封類
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 17:20
 */
public sealed class Daddy permits Son1, Son2 {
}

第一個兒子可以繼承

/**
 * <p>
 *
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 17:22
 */
public final class Son1 extends Daddy {
}

第二個兒子可以繼承

/**
 * <p>
 *
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 17:23
 */
public final class Son2 extends Daddy {
}

第三個兒子估計是不孝子

可以看到IDEA是有錯誤提示的,意思是沒有被sealed宣告的父類別所允許。

然後,我們來看看密封介面。

其實和密封類差不多,但還可以結合前面講過的record來簡化程式碼。

/**
 * <p>
 * JDK17新特性:密封介面
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 18:03
 */
public sealed interface DaddyInterface permits Son4, Son5 {
   void test();
}

第四個兒子可以實現父親的願望,用了record之後簡化了變數宣告及一些內建方法的實現。

/**
 * <p>
 *
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 17:23
 */
public record Son4(int age, String name) implements DaddyInterface {
   @Override
   public void test() {

   }
}

第五個兒子可以實現父親的願望

/**
 * <p>
 *
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/2 17:23
 */
public record Son5(int age, String name) implements DaddyInterface {
   @Override
   public void test() {

   }
}

第六個兒子有點傻,不能實現父親的願望,可以看到錯誤提示和前面密封類一樣。

3)、注意

1)、sealed宣告的父類別,它的子類只能用final、sealed、non-sealed來修飾;

2)、sealed宣告的父類別,至少要有一個子類;

3)、沒有在permits中授權的子類,無法繼承父類別;

4)、密封介面和密封類的注意點沒什麼區別;

4)、密封介面結合record來完成可以少寫更多程式碼變得更加簡潔。


這裡特別說一點,sealed和record其實在Java新特性模式匹配中很有意義,但是我認為模式匹配對於從未了解過的Java程式設計師來講有些晦澀,結合sealed後有種套娃傳銷的感覺,如果難於理解就不是我們本來的目的,因此沒有專門花費章節贅述,結尾會簡單說一下我對模式匹配的看法。


8、switch增強

1)、說明

為什麼把switch單獨放最後講,因為Java14-17分別對其做了增強,放在最後彙總起來講更直觀。

一共有三個改變:

1)、支援箭頭語法;

2)、支援表示式;

3)、支援case null。

2)、案例

箭頭語法,其實就是把舊寫法中的冒號和break直接換成了箭頭來代替,更簡潔。

/**
 * <p>
 * JDK14新特性:switch箭頭語法
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/3 11:05
 */
public class SwitchDemo1 {
   private static void foods(int i) {
      switch (i) {
         case 1:
            System.err.println("青椒肉絲");
            break;
         case 2:
            System.err.println("番茄炒蛋");
            break;
         default:
            System.err.println("米飯");
      }
   }

   private static void fruits(int i) {
      switch (i) {
         case 1 -> System.err.println("香蕉");
         case 2 -> System.err.println("獼猴桃");
         default -> System.err.println("蘋果");
      }
   }

   public static void main(String[] args) {
      foods(1);
      fruits(3);
   }
}

支援表示式,其實就是能寫單個或多個表示式來return。

/**
 * <p>
 * JDK14新特性:switch支援表示式
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/3 11:05
 */
public class SwitchDemo3 {

   /**
    * 單表示式
    */
   private static String fruits(int i) {
      return switch (i) {
         case 1 -> "香蕉";
         case 2 -> "獼猴桃";
         default -> "蘋果";
      };
   }

   /**
    * 多表示式
    */
   private static String fruitsNew(int i) {
      return switch (i) {
         case 1, 2 -> {
            System.err.println("----------------------------------");
            System.err.println("來一個香蕉");
            yield "香蕉來咯";
         }
         case 3, 4 -> {
            System.err.println("----------------------------------");
            System.err.println("來一個獼猴桃");
            yield"獼猴桃來咯";
         }
         default -> {
            System.err.println("----------------------------------");
            System.err.println("沒的選就來個蘋果吧");
            yield "蘋果來咯";
         }
      };
   }

   public static void main(String[] args) {
//    System.err.println(fruits(2));
//    System.err.println(fruits(3));

      System.err.println(fruitsNew(2));
      System.err.println(fruitsNew(4));
      System.err.println(fruitsNew(5));
   }
}

支援case null,其實就是case可以接收null值了。

/**
 * <p>
 * JDK17新特性(預覽):switch支援case null
 * </p>
 *
 * @author 程式設計師濟癲,公眾號:【Java分享客棧】
 * @since 2022/12/3 11:05
 */
public class SwitchDemo2 {
   private static void foods(String s) {
      if (s == null) {
         return;
      }
      switch (s) {
         case "1":
            System.err.println("青椒肉絲");
            break;
         case "2":
            System.err.println("番茄炒蛋");
            break;
         default:
            System.err.println("米飯");
      }
   }

   private static void fruits(String s) {
      switch (s) {
         case "1" -> System.err.println("香蕉");
         case null -> System.err.println("null");
         default -> System.err.println("蘋果");
      }
   }

   public static void main(String[] args) {
      foods("1");
      fruits(null);
   }
}

可以看下效果,直接傳null也不會報錯。

3)、注意

1)、箭頭語法,冒號和箭頭不能同時存在;

2)、表示式,多個表示式時要使用花括號並使用yield關鍵字返回;

3)、case null是預覽功能,在IDEA - Project Structure - Modules中選擇Language Level為17(Preview)才能編譯通過,參考前面的準備工作。


總結

1)、Java9-17的新特性不僅於此,還有一些挺有特點的內容,比如不可變集合、模組化、String和Stream的API增強等等,但是我個人認為不具有代表性,要麼是工具能直接幫你轉換,要麼就是你大概率用不到,所以就沒列出來;


2)、模式匹配,是不少Java程式設計師關注的內容,本篇中record、switch、密封類和介面的內容其實都是模式匹配的基礎,但模式匹配對Java來講並不成熟,《Thinking In Java》的作者也說過可能要好幾年才會看到完整形式的Java模式匹配,所以沒必要現在就花太多心思去研究一個殘缺版本,這個特性和Python、Kotlin、Scala相比其實還差得遠。


Java發展趨勢

最後稍微說下不少人關心的這個問題,我覺得只要你瞭解過Java8之後這些版本的新特性和預覽特性,你一定可以發現Java在嘗試改變,這是一個很好的訊號。


就比如上面的這些新特性,你甚至能找到不少Python、JavaScript等語言的影子,Go語言作為新語言就是站在巨人肩膀上發展起來的,吸納了很多語言的優秀特點。


現在,Java也在走類似的路,能明顯看到它在將一些優秀語言的亮點容納到自己的新版本中,這種趨勢代表著一個意義:Java在不斷進步。


網上一直充斥著一些看衰Java的言論,沒必要當真,你必須自己去體會生態和了解國內的IT公司動態才能有所感受。


Java依然是國內使用最廣泛的語言,並且具備最龐大的生態,這不是一朝一夕可以替代的,是市場規律發展的結果。


SpringBoot3.0支援Java17,Jenkins新版支援Java17,Kafka3.0直接拋棄Java8,還有IDEA2022預設支援Java17,等等之類的開源社群和生態都在給新版的Java背書,更有微軟宣佈全面擁抱Java,這裡面不單單是技術層面的提高,更有利益的訴求和捆綁。


從這一點來說,學習Java完全是值得的,作為一門成熟優秀且嚴謹的語言,它就像一個白領一樣正襟危坐。


我還認為現在學習和掌握Java的工程師,未來轉去其他語言也不會有壓力,上面的新特性就說明了這種方向,它在吸納其他語言的亮點。


未來可以預期的是,你轉到其他語言會逐漸變的沒有那麼陌生和晦澀,這是我看到的一個方向,未來的程式語言發展大方向就是大融合(大抄襲,咳咳),你中有我,我中有你。



原創文章純手打,一個一個字敲出來的,鍵盤上全是血,如果覺得有幫助麻煩點個推薦吧~

本人致力於分享工作中的經驗及趣事,喜歡的話可以進主頁關注一下哦~