成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

要不要升級?Java 21強大的新特性,代碼量減半

開發 前端
FMT 是 Java 平臺定義的另一種模板處理器。FMT 與 STR 類似,它執行插值,但也解釋嵌入式表達式左側的格式規范。格式說明符與 java.util.Formatter 中定義的格式說明符相同。

1. record模式

Record模式由 JEP 405 作為預覽功能提出,并在 JDK 19 中發布,JEP 432 進行了第二次預覽,并在 JDK 20 中發布。該功能與用于switch的模式匹配(JEP 441)共同發展,兩者之間有相當多的交互

1.1 instanceof類型模式

Object obj = "Pack" ;
// Java 16之前
if (obj instanceof String) {
    String s = (String) obj ;
    System.out.println("強轉為String") ;
}
// 自Java 16起
if (obj instanceof String s) {
    System.out.println("簡便多了") ;
}

在上面的代碼中從java16開始,運行時obj的值是String的實例,則obj與類型模式String s匹配。如果模式匹配,則表達式的實例為true,并且模式變量s初始化為obj轉換為String的值,然后可以在包含的代碼塊中使用該值。

1.2 模式匹配與Records

Records (JEP 395)是數據的透明載體。接收record類實例的代碼通常將使用內置的組件訪問器方法提取數據,稱為組件。例如,我們可以使用類型模式來測試值是否是record類Point的實例,如果是,則從值中提取x和y組件:

// 自Java 16起
public record Point(int x, int y) {
}
public static void main(String[] args) {
  Object obj = new Point(10, 20);
  if (obj instanceof Point p) {
    int x = p.x();
    int y = p.y();
    System.out.println(x + y);
  }
}

上面的代碼看著與1.1中介紹的沒撒區別就是類型模式,在上面的代碼中我們僅僅是訪問了record類x與y的方法,如果是這樣我們還可以像下面這樣操作:

Object obj = new Point(10, 20) ;
// 自java 21起
if (obj instanceof Point(int x, int y)) {
  System.out.println(x + y) ;
}

這里的Point(int x, int y) 是一個record模式。它將提取組件的局部變量聲明移至模式本身,并在值與模式匹配時通過調用訪問器方法初始化這些變量。

1.3 嵌套record模式

有如下定義

public record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}

如果要提取左上角點的顏色,我們可以這樣寫:

Object r = new Rectangle(
    new ColoredPoint(new Point(0, 0), Color.RED), 
    new ColoredPoint(new Point(100, 100), Color.BLUE)
  ) ;
// 從java 21起  
if (r instanceof Rectangle(ColoredPoint ul, ColoredPoint lr)) {
  System.out.printf("%s, %s%n", ul, lr) ;
}

輸出結果

ColoredPoint[p=Point[x=0, y=0], c=RED], ColoredPoint[p=Point[x=100, y=100], c=BLUE]

如果你希望訪問具體的顏色值,record模式還支持嵌套,如下示例:

// 從java 21起
if (r instanceof Rectangle(
    ColoredPoint(Point(int x, int y), Color c1), 
    ColoredPoint lr
  )
) {
  System.out.printf("x = %d, y = %d%n", x, y) ;
}

1.4 嵌套模式無法匹配情況

在下面這情況下是無法進行匹配的

public record Pair(Object x, Object y) {}
Pair p = new Pair(42, 42);
if (p instanceof Pair(String s, String t)) {
  System.out.println(s + ", " + t);
} else {
  System.out.println("Not a pair of strings") ;
}

以上是關于record 模式的所有內容。

2. switch模式匹配

該功能最初由 JEP 406(JDK 17)提出,后經 JEP 420(JDK 18)、427(JDK 19)和 433(JDK 20)改進。它與 "1. record模式 "功能(JEP 440)共同發展。

先來看下如下這段代碼

Object obj = 100L ;
if (obj instanceof Integer) {
  Integer i = (Integer) obj ;
  obj = String.format("int %d", i);
} else if (obj instanceof Long) {
  Long l = (Long) obj ;
  obj = String.format("long %d", l);
} else if (obj instanceof String) {
  String s = (String) obj ;
  obj = String.format("String %s", s);
}

有個instanceof 模式以后就可以簡化這樣了

Object obj = 100L ;
if (obj instanceof Integer i) {
  obj = String.format("int %d", i);
} else if (obj instanceof Long l) {
  obj = String.format("long %d", l);
} else if (obj instanceof String s) {
  obj = String.format("String %s", s);
}
System.out.printf("result obj = %s%n", obj) ;

注意:上面的代碼有2個問題

  1. 上面的代碼有如果沒有編譯器的作用,那么它的時間復雜度將是O(n)
  2. 隱藏了一個BUG,當if,else沒有判斷到某個類型時可能會出現問題上面的代碼并沒有else,因為不強制所以當判斷遺漏了某種類型時可能會給程序帶來潛在的問題。

從Java 21開始,我們可以如下處理上面的if.. else 

var ret = switch (obj) {
  case Integer i -> String.format("int %d", i);
  case Long l    -> String.format("long %d", l);
  case String s  -> String.format("String %s", s);
  default        -> obj.toString() ;
};
System.out.printf("result ret = %s%n", ret) ;

在過去我們知道如果switch的每個case沒有break或者return,那么它會穿透到下一個case直到遇到break或return。并且在傳統的switch中沒有default也是可以的。但是在上面的代碼中必須要有default子句。

2.1 switch與null值

傳統上,如果switch表達式值為空,switch 語句和表達式會拋出 NullPointerException,因此必須在 switch 之外進行空判斷:

String s = null ;
switch (s) {
  // 如果不清楚這里的語法,你應該先看看java14對switch新語法的介紹
  case "a", "b" -> System.out.println("a or b") ;
  default -> System.out.println("defualt value") ;
}

控制臺輸出

圖片圖片

在上面的代碼中在過去,我們要先對s進行null的判斷,再進行switch,否則有可能就會出現上面的錯誤。修改如下:

if (s == null) {
  return ;
}
switch (s) {
  // TODO
}

以上代碼是Java 21之前,從Java 21起,我們可以如下:

switch (s) {
  case null -> System.out.println("oops") ;
  case "a", "b" -> System.out.println("a or b") ;
  default -> System.out.println("defualt value") ;
}

無需單獨的if判斷是否為null情況。

2.2 switch條件判斷

在case中還可以添加if...else判斷

static void fn1(String resp) {
  switch (resp) {
    case String s -> {
      if (s.equalsIgnoreCase("success"))
        System.out.println("處理成功");
      else if (s.equalsIgnoreCase("failure"))
        System.err.println("處理失敗");
      else
        System.out.println("未知結果") ;
    }
  }
}

在case中是使用when子句

static void fn2(String resp) {
  switch (resp) {
    case null -> {}
    case String s 
    when s.equalsIgnoreCase("success") -> {
      System.out.println("處理成功");
    }
    case String s
    when s.equalsIgnoreCase("failure") -> {
      System.err.println("處理失敗");
    }
    case String s -> {
        System.out.println("未知結果") ;
    }
  }
}

這樣,switch的可讀性就更強了。

2.3 switch與enum常量

在Java 21之前,switch的case表達式必須是枚舉類型,標簽必須是枚舉常量的簡單名稱,如下示例:

public enum Color { RED, BLUE, GREEN }
public static void fn1(Color c) {
  switch (c) {
    case RED, BLUE -> System.out.println("我喜歡的顏色") ;
    case GREEN -> {
      // TODO
    }
    default -> System.out.println("我討厭的顏色") ;
  }
}

上面說的標簽必須是枚舉常量的簡單名稱什么意思呢?就是說在java21之前使用枚舉時的標簽不能是下面這種寫法:

case Color.GREEN -> {}

而從Java 21起可以使用這種語法。

3. 虛擬線程

關于虛擬線程請查看這篇文章:

【技術革命】JDK21虛擬線程來襲,讓系統的吞吐量翻倍!

4. 字符串模版

注:這是一個預覽功能

編譯:javac --enable-preview --source 21 -Xlint:preview Xxx.java

運行:java --enable-preview Xxx

在開發中字符串相關的操作是非常非常多的,雖然Java 提供了多種字符串組成機制,但遺憾的是,所有機制都有缺點。

  • 使用+操作符拼接字符串,看著都不好理解
String result = x + " + " + y + " = " + (x + y) ;
  • 冗余的StringBuilder
String s = new StringBuilder().append(x).append(" + ")
  .append(y).append(" = ").append(x + y).toString() ;
  • String#format 與 String#formatted將格式字符串與參數分離,避免了類型錯配:
int x = 10, y = 20 ;
String s = String.format("%2$d + %1$d = %3$d", x, y, x + y);
String t = "%2$d + %1$d = %3$d".formatted(x, y, x + y) ;
  • java.text.MessageFormat要求太多,而且格式字符串中使用了不熟悉的語法:
String ret = MessageFormat.format("{0} + {1} = {2}", x, y, x + y) ;

4.1 STR 模板處理器

STR 是 Java 平臺定義的模板處理器。它通過用表達式的(字符串化)值替換模板中的每個嵌入表達式來執行字符串插值。

String firstName = "Bill" ;
String lastName  = "Duck" ;
String fullName  = STR."\{firstName} \{lastName}" ;
System.out.println(fullName) ;

輸出結果

Bill Duck

注:STR 是一個公共靜態最終字段,會自動導入到每個 Java 源文件中。

表達式還可以執行相應的操作,如下:

int x = 10, y = 20 ;
String result = STR."\{x} + \{y} = \{x + y}" ;
System.out.println(result) ;
// 10 + 20 = 30

表達式中還可以調用方法

static String getName() {
  return "張三" ;
}
static record Req(String date, String time) {}
static void fn5() {
  String s = STR."我的名字是 \{getName()} ";
  System.out.println(s) ;
  Req req = new Req("2000-01-01", "23:59:59") ;
  String t = STR."Access at \{req.date} \{req.time}";
  System.out.println(t) ;
}

輸出結果

我的名字是 張三
Access at 2000-01-01 23:59:59

多行模版字符串

static void fn6() {
  String name    = "張三";
  String phone   = "1899999999";
  String address = "xxxooo";
  String json = STR."""
  {
    "name":    "\{name}",
    "phone":   "\{phone}",
    "address": "\{address}"
  }
  """;
  System.out.println(json);
}

輸出結果

{
   "name": "張三",
   "phone": "1899999999",
   "address": "xxxooo"
}

以上是基于STR模版處理器的內容,接下來介紹另外一個。

4.2 FMT 模板處理器

FMT 是 Java 平臺定義的另一種模板處理器。FMT 與 STR 類似,它執行插值,但也解釋嵌入式表達式左側的格式規范。格式說明符與 java.util.Formatter 中定義的格式說明符相同。

record Rectangle(String name, double width, double height) {
  double area() {
    return width * height;
  }
}
public static void main(String[] args) {
  Rectangle[] zone = new Rectangle[] {
    new Rectangle("Alfa", 17.8, 31.4),
    new Rectangle("Bravo", 9.6, 12.4),
  };
  String s = FMT."""
    Description     Width    Height     Area
    %-12s\{zone[0].name}  %7.2f\{zone[0].width}  %7.2f\{zone[0].height}     %7.2f\{zone[0].area()}
    %-12s\{zone[1].name}  %7.2f\{zone[1].width}  %7.2f\{zone[1].height}     %7.2f\{zone[1].area()}
    \{" ".repeat(28)} Total %7.2f\{zone[0].area() + zone[1].area() + zone[2].area()}
  """;
  System.out.println(s) ;
}

5. 序列集合

在Java21 之前的集合類中要獲取第一個和最后一個元素,不同的集合操作方式不同或者壓根就沒有對應的方法。如下示例:

圖片圖片

在說遍歷集合,正向時(從第一個到最后一個)操作方法基本一致。但是反向時遍歷時每個集合就又不相同了。

在JDK21中提供了如下3個序列接口:

  • SequencedCollection
public interface SequencedCollection<E> extends Collection<E> {
  SequencedCollection<E> reversed() ;
  default void addFirst(E e) ;
  default void addLast(E e) ;
  default E getFirst() ;
  default E getLast() ;
  default E removeFirst() ;
  default E removeLast() ;
}
  • SequencedSet
public interface SequencedSet<E> extends SequencedCollection<E>, Set<E> {
  SequencedSet<E> reversed();
}
  • SequencedMap
public interface SequencedMap<K, V> extends Map<K, V> {
  SequencedMap<K, V> reversed() ;
  default Map.Entry<K,V> firstEntry() ;
  default Map.Entry<K,V> lastEntry() ;
  default Map.Entry<K,V> pollFirstEntry() ;
  default Map.Entry<K,V> pollLastEntry() ;
  default V putFirst(K k, V v) ;
  default V putLast(K k, V v) ;
  // other
}

以上3個集合都提供了對應的獲取第一個和最后一個元素的方法及集合反轉方法。上面定義的三個新接口與現有的集合類型層次結構非常吻合,如下圖:

圖片圖片

對現有的類和接口進行了如下調整:

  • List 現在將 SequencedCollection 作為其直接超接口、
  • Deque 現在將 SequencedCollection 作為其直接超接口、
  • LinkedHashSet 進一步實現了 SequencedSet、
  • SortedSet 現在將 SequencedSet 作為其直接超接口、
  • LinkedHashMap 進一步實現了 SequencedMap,而
  • SortedMap 現在將 SequencedMap 作為其直接超接口。

6. 未命名模式&變量

注:這是一個預覽功能

先看下面這個示例

public record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint cp) {}
  
Object obj = new Rectangle(
    new ColoredPoint(new Point(10, 10), Color.RED)
  ) ;
if (obj instanceof Rectangle(ColoredPoint(Point(int x, int y), Color c))) {
  System.out.printf("x = %d, y = %d%n", x, y) ;
}

在上面的if判斷中,對于Color c變量并沒有使用,從Java 21開始我們可以像下面這樣改寫:

if (obj instanceof Rectangle(ColoredPoint(Point(int x, int y), _))) {
  System.out.printf("x = %d, y = %d%n", x, y) ;
}

使用一個 "_" 下劃線代替即可。

未使用的變量

int[] arr = {1, 2, 3, 4, 5} ;
int total = 0 ;
for (var a : arr) {
  total++ ;
}

在這個示例中,變量a并沒有使用,所以從Java 21開始可以改寫如下:

for (var _ : arr) {
  total++ ;
}

對于這樣沒有使用的變量,我們可以用一個 "_" 下劃線代替。其它示例:

try {
  int a = 1 / 0 ;
} catch (Exception _) { // 這里沒有用到異常通過可以使用 _
}

注:我用的Eclipse沒法直接使用,我這里是通過記事本編寫,通過命令行編譯&運行。

7. 未命名的類&Main方法

注:這是一個預覽功能

下面這個代碼是學習java的入門代碼

public class UnnamedClassAndMain {
 public static void main(String[] args) {
   System.out.println("Hello World!!!") ;
 }
}

從Java 21開始,我們可以簡化成如下形式了

public class UnnamedClassAndMain {
  void main() {
    System.out.println("Hello World!!!") ;
  }
}

未命名的類

還是拿上面的程序演示,我們還可以繼續簡化如下形式:

void main() {
  System.out.println("Hello World!!!") ;
}

對,文件中只有一個極簡的方法,連類的聲明都沒有了。你甚至還可以如下,定義方法,方法調用

String name = "Pack" ;
String getName() {
  return name ;
}
void main() {
  System.out.println(getName()) ;
}

類文件直接定義方法,聲明變量。

責任編輯:武曉燕 來源: Spring全家桶實戰案例源碼
相關推薦

2013-11-29 14:04:42

2012-03-16 14:23:00

框架

2018-11-19 09:02:53

垃圾代碼辭職入職

2023-10-06 12:04:41

ORM關系型數據庫

2017-11-20 09:00:43

跳槽職場精英年終獎

2012-03-16 14:23:00

框架

2021-11-24 22:39:03

手機系統功能

2021-02-21 09:09:24

GoGOPATH代碼

2019-10-23 20:19:26

Python 開發編程語言

2020-11-17 09:17:58

框架組件基礎服務

2018-01-24 07:58:47

框架組件技術棧開源

2018-07-12 09:04:15

RAID陣列硬盤

2014-12-09 09:13:46

BaaS云備份備份即服務

2015-10-22 10:57:12

Facebook功能

2020-06-11 14:07:44

iOS 13.6.2蘋果手機

2021-10-21 09:41:43

互聯網數據技術

2018-01-29 09:42:27

創業技術團隊

2020-10-20 10:53:01

5G套餐運營商5G

2018-12-06 14:40:07

蘋果iOS 12.1.1 iOS 12
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品www| 日韩在线观看中文字幕 | 日韩久久久久久 | 国产乱码精品一区二区三区中文 | 国产精品一区久久久 | 午夜99| 日韩在线一区二区三区 | 中文字幕一区二区在线观看 | 日韩一区二区黄色片 | av天天澡天天爽天天av | 久久综合狠狠综合久久综合88 | 一区二区三区在线看 | 黄网免费看 | 国产高清视频 | 久久亚洲欧美日韩精品专区 | 欧美亚洲视频 | 久久久久久毛片免费观看 | 久久久久成人精品免费播放动漫 | 国产高清一区二区三区 | 自拍偷拍3p | av在线天堂| 成年人在线电影 | 亚洲精品一区二区三区蜜桃久 | 欧美在线日韩 | 欧美日韩免费一区二区三区 | 亚洲电影在线播放 | 久久久精品一区二区 | 9久久婷婷国产综合精品性色 | 日韩高清三区 | 好姑娘高清在线观看电影 | 精品国产乱码久久久久久闺蜜 | 欧美日韩在线一区二区三区 | 久久99精品久久久久久青青日本 | 国产精品自产av一区二区三区 | 亚洲高清一区二区三区 | 欧美一级片中文字幕 | 亚洲国产区 | 国产中文字幕在线 | 久久国产精品99久久久大便 | 这里精品 | 做a视频 |