自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

要不要升級(jí)?Java 21強(qiáng)大的新特性,代碼量減半

開發(fā) 前端
FMT 是 Java 平臺(tái)定義的另一種模板處理器。FMT 與 STR 類似,它執(zhí)行插值,但也解釋嵌入式表達(dá)式左側(cè)的格式規(guī)范。格式說明符與 java.util.Formatter 中定義的格式說明符相同。

1. record模式

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

1.1 instanceof類型模式

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

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

1.2 模式匹配與Records

Records (JEP 395)是數(shù)據(jù)的透明載體。接收record類實(shí)例的代碼通常將使用內(nèi)置的組件訪問器方法提取數(shù)據(jù),稱為組件。例如,我們可以使用類型模式來測(cè)試值是否是record類Point的實(shí)例,如果是,則從值中提取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中介紹的沒撒區(qū)別就是類型模式,在上面的代碼中我們僅僅是訪問了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) 是一個(gè)record模式。它將提取組件的局部變量聲明移至模式本身,并在值與模式匹配時(shí)通過調(diào)用訪問器方法初始化這些變量。

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) {}

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

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) ;
}

輸出結(jié)果

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 嵌套模式無法匹配情況

在下面這情況下是無法進(jìn)行匹配的

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") ;
}

以上是關(guān)于record 模式的所有內(nèi)容。

2. switch模式匹配

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

先來看下如下這段代碼

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);
}

有個(gè)instanceof 模式以后就可以簡(jiǎn)化這樣了

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個(gè)問題

  1. 上面的代碼有如果沒有編譯器的作用,那么它的時(shí)間復(fù)雜度將是O(n)
  2. 隱藏了一個(gè)BUG,當(dāng)if,else沒有判斷到某個(gè)類型時(shí)可能會(huì)出現(xiàn)問題上面的代碼并沒有else,因?yàn)椴粡?qiáng)制所以當(dāng)判斷遺漏了某種類型時(shí)可能會(huì)給程序帶來潛在的問題。

從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的每個(gè)case沒有break或者return,那么它會(huì)穿透到下一個(gè)case直到遇到break或return。并且在傳統(tǒng)的switch中沒有default也是可以的。但是在上面的代碼中必須要有default子句。

2.1 switch與null值

傳統(tǒng)上,如果switch表達(dá)式值為空,switch 語句和表達(dá)式會(huì)拋出 NullPointerException,因此必須在 switch 之外進(jìn)行空判斷:

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

控制臺(tái)輸出

圖片圖片

在上面的代碼中在過去,我們要先對(duì)s進(jìn)行null的判斷,再進(jìn)行switch,否則有可能就會(huì)出現(xiàn)上面的錯(cuò)誤。修改如下:

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") ;
}

無需單獨(dú)的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("未知結(jié)果") ;
    }
  }
}

在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("未知結(jié)果") ;
    }
  }
}

這樣,switch的可讀性就更強(qiáng)了。

2.3 switch與enum常量

在Java 21之前,switch的case表達(dá)式必須是枚舉類型,標(biāo)簽必須是枚舉常量的簡(jiǎn)單名稱,如下示例:

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("我討厭的顏色") ;
  }
}

上面說的標(biāo)簽必須是枚舉常量的簡(jiǎn)單名稱什么意思呢?就是說在java21之前使用枚舉時(shí)的標(biāo)簽不能是下面這種寫法:

case Color.GREEN -> {}

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

3. 虛擬線程

關(guān)于虛擬線程請(qǐng)查看這篇文章:

【技術(shù)革命】JDK21虛擬線程來襲,讓系統(tǒng)的吞吐量翻倍!

4. 字符串模版

注:這是一個(gè)預(yù)覽功能

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

運(yùn)行:java --enable-preview Xxx

在開發(fā)中字符串相關(guān)的操作是非常非常多的,雖然Java 提供了多種字符串組成機(jī)制,但遺憾的是,所有機(jī)制都有缺點(diǎn)。

  • 使用+操作符拼接字符串,看著都不好理解
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將格式字符串與參數(shù)分離,避免了類型錯(cuò)配:
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 平臺(tái)定義的模板處理器。它通過用表達(dá)式的(字符串化)值替換模板中的每個(gè)嵌入表達(dá)式來執(zhí)行字符串插值。

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

輸出結(jié)果

Bill Duck

注:STR 是一個(gè)公共靜態(tài)最終字段,會(huì)自動(dòng)導(dǎo)入到每個(gè) Java 源文件中。

表達(dá)式還可以執(zhí)行相應(yīng)的操作,如下:

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

表達(dá)式中還可以調(diào)用方法

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) ;
}

輸出結(jié)果

我的名字是 張三
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);
}

輸出結(jié)果

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

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

4.2 FMT 模板處理器

FMT 是 Java 平臺(tái)定義的另一種模板處理器。FMT 與 STR 類似,它執(zhí)行插值,但也解釋嵌入式表達(dá)式左側(cè)的格式規(guī)范。格式說明符與 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 之前的集合類中要獲取第一個(gè)和最后一個(gè)元素,不同的集合操作方式不同或者壓根就沒有對(duì)應(yīng)的方法。如下示例:

圖片圖片

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

在JDK21中提供了如下3個(gè)序列接口:

  • 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個(gè)集合都提供了對(duì)應(yīng)的獲取第一個(gè)和最后一個(gè)元素的方法及集合反轉(zhuǎn)方法。上面定義的三個(gè)新接口與現(xiàn)有的集合類型層次結(jié)構(gòu)非常吻合,如下圖:

圖片圖片

對(duì)現(xiàn)有的類和接口進(jìn)行了如下調(diào)整:

  • List 現(xiàn)在將 SequencedCollection 作為其直接超接口、
  • Deque 現(xiàn)在將 SequencedCollection 作為其直接超接口、
  • LinkedHashSet 進(jìn)一步實(shí)現(xiàn)了 SequencedSet、
  • SortedSet 現(xiàn)在將 SequencedSet 作為其直接超接口、
  • LinkedHashMap 進(jìn)一步實(shí)現(xiàn)了 SequencedMap,而
  • SortedMap 現(xiàn)在將 SequencedMap 作為其直接超接口。

6. 未命名模式&變量

注:這是一個(gè)預(yù)覽功能

先看下面這個(gè)示例

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判斷中,對(duì)于Color c變量并沒有使用,從Java 21開始我們可以像下面這樣改寫:

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

使用一個(gè) "_" 下劃線代替即可。

未使用的變量

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

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

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

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

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

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

7. 未命名的類&Main方法

注:這是一個(gè)預(yù)覽功能

下面這個(gè)代碼是學(xué)習(xí)java的入門代碼

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

從Java 21開始,我們可以簡(jiǎn)化成如下形式了

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

未命名的類

還是拿上面的程序演示,我們還可以繼續(xù)簡(jiǎn)化如下形式:

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

對(duì),文件中只有一個(gè)極簡(jiǎn)的方法,連類的聲明都沒有了。你甚至還可以如下,定義方法,方法調(diào)用

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

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

責(zé)任編輯:武曉燕 來源: Spring全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2013-11-29 14:04:42

2018-11-19 09:02:53

垃圾代碼辭職入職

2012-03-16 14:23:00

框架

2012-03-16 14:23:00

框架

2017-11-20 09:00:43

跳槽職場(chǎng)精英年終獎(jiǎng)

2023-10-06 12:04:41

ORM關(guān)系型數(shù)據(jù)庫

2021-11-24 22:39:03

手機(jī)系統(tǒng)功能

2021-02-21 09:09:24

GoGOPATH代碼

2019-10-23 20:19:26

Python 開發(fā)編程語言

2020-06-11 14:07:44

iOS 13.6.2蘋果手機(jī)

2018-01-24 07:58:47

框架組件技術(shù)棧開源

2014-12-09 09:13:46

BaaS云備份備份即服務(wù)

2020-11-17 09:17:58

框架組件基礎(chǔ)服務(wù)

2018-07-12 09:04:15

RAID陣列硬盤

2021-10-21 09:41:43

互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)

2020-10-20 10:53:01

5G套餐運(yùn)營商5G

2015-10-22 10:57:12

Facebook功能

2018-01-29 09:42:27

創(chuàng)業(yè)技術(shù)團(tuán)隊(duì)

2015-11-30 11:52:03

云服務(wù)最佳實(shí)踐
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)