日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java 17的一些新特性

發(fā)布時(shí)間:2024/5/14 java 61 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 17的一些新特性 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Java 17是Java 11以來又一個(gè)LTS(長期支持)版本,Java 11 和Java 17之間發(fā)生了那些變化可以在OpenJDK官網(wǎng)找到JEP(Java增強(qiáng)建議)的完整列表。

文章會(huì)重點(diǎn)介紹在語法方面Java 17的更新,并且通過一些代碼示例讓大家更容易理解,主要涉及以下9個(gè)點(diǎn):

  • 文本塊
  • switch表達(dá)式
  • record關(guān)鍵字?
  • ?密封類 sealed class
  • instanceof模式匹配?
  • Helpful NullPointerExceptions
  • 日期周期格式化?
  • 精簡數(shù)字格式化支持
  • Stream.toList()?

文本塊

在Java17之前的版本里,如果我們需要定義一個(gè)字符串,比如一個(gè)JSON數(shù)據(jù),基本都是如下方式定義:

public void lowVersion() {String text = "{\n" +" \"name\": \"小黑說Java\",\n" +" \"age\": 18,\n" +" \"address\": \"北京市西城區(qū)\"\n" +"}";System.out.println(text); }

這種方式定義具有幾個(gè)問題:

  • 雙引號(hào)需要進(jìn)行轉(zhuǎn)義;
  • 為了字符串的可讀性需要通過+號(hào)連接;
  • 如果需要將JSON復(fù)制到代碼中需要做大量的格式調(diào)整(當(dāng)然這一點(diǎn)也可以通過其他工具解決);

通過Java 17中的文本塊語法,類似的字符串處理則會(huì)方便很多;通過三個(gè)雙引號(hào)可以定義一個(gè)文本塊,并且結(jié)束的三個(gè)雙引號(hào)不能和開始的在同一行。

上面例子中的JSON可以更方便,可讀性更好的通過文本塊定義。代碼如下:

private void highVersion() {String text = """{"name": "小黑說Java","age": 18,"address": "北京市西城區(qū)"}""";System.out.println(text); }

這段代碼的輸出結(jié)果是:

{"name": "小黑說Java","age": 18,"address": "北京市西城區(qū)" }

switch表達(dá)式

Java 17版本中switch表達(dá)式將允許switch有返回值,并且可以直接作為結(jié)果賦值給一個(gè)變量,等等一系列的變化。

下面有一個(gè)switch例子,依賴于給定的枚舉值,執(zhí)行case操作,故意省略break。

private static void lowVesion(Fruit fruit) {switch (fruit) {case APPLE, PEAR:System.out.println("普通水果");case MANGO, AVOCADO:System.out.println("進(jìn)口水果");default:System.out.println("未知水果");} }

我們調(diào)用這個(gè)方法傳入一個(gè)APPLE,會(huì)輸出以下結(jié)果:

普通水果 進(jìn)口水果 未知水果

?顯然這不是期望的結(jié)果,因?yàn)槲覀冃枰诿總€(gè)case里添加break防止所有的case都沒執(zhí)行。

private static void lowVesion(Fruit fruit) {switch (fruit) {case APPLE, PEAR:System.out.println("普通水果");break;case MANGO, AVOCADO:System.out.println("進(jìn)口水果");break;default:System.out.println("未知水果");} }

可以通過switch表達(dá)式來進(jìn)行簡化。將冒號(hào)(:)替換為箭頭(->),并且switch表達(dá)式默認(rèn)不會(huì)失敗,所以不需要break。

private static void withSwitchExpression(Fruit fruit) {switch (fruit) {case APPLE, PEAR -> System.out.println("普通水果");case MANGO, AVOCADO -> System.out.println("進(jìn)口水果");default -> System.out.println("未知水果");} }

switch表達(dá)式也可以返回一個(gè)值,比如上面的例子我們可以讓switch返回一個(gè)字符串來表示我們要打印的文本。需要注意在switch語句的最后要加一個(gè)分號(hào)。

private static void withReturnValue(Fruit fruit) {String text = switch (fruit) {case APPLE, PEAR -> "普通水果";case MANGO, AVOCADO -> "進(jìn)口水果";default -> "未知水果";};System.out.println(text); }

也可以直接省略賦值動(dòng)作直接打印。

private static void withReturnValue(Fruit fruit) {System.out.println(switch (fruit) {case APPLE, PEAR -> "普通水果";case MANGO, AVOCADO -> "進(jìn)口水果";default -> "未知水果";}); }

如果你想在case里想做不止一件事,比如在返回之前先進(jìn)行一些計(jì)算或者打印操作,可以通過大括號(hào)來作為case塊,最后的返回值使用關(guān)鍵字yield進(jìn)行返回。

private static void withYield(Fruit fruit) {String text = switch (fruit) {case APPLE, PEAR -> {System.out.println("給的水果是: " + fruit);yield "普通水果";}case MANGO, AVOCADO -> "進(jìn)口水果";default -> "未知水果";};System.out.println(text); }

這個(gè)輸出結(jié)果是:

給的水果是: APPLE 普通水果

當(dāng)然也可以直接使用yield返回結(jié)果。

private static void oldStyleWithYield(Fruit fruit) {System.out.println(switch (fruit) {case APPLE, PEAR:yield "普通水果";case MANGO, AVOCADO:yield "進(jìn)口水果";default:yield "未知水果";}); }

record關(guān)鍵字?

record用于創(chuàng)建不可變的數(shù)據(jù)類。在這之前如果你需要?jiǎng)?chuàng)建一個(gè)存放數(shù)據(jù)的類,通常需要先創(chuàng)建一個(gè)Class,然后生成構(gòu)造方法、getter、setter、hashCode、equals和toString等這些方法,或者使用Lombok來簡化這些操作。

比如定義一個(gè)Person類:

// 這里使用lombok減少代碼 @Data @AllArgsConstructor public class Person {private String name;private int age;private String address; }

我們來通過Person類做一些測(cè)試,比如創(chuàng)建兩個(gè)對(duì)象,對(duì)他們進(jìn)行比較,打印這些操作。

public static void testPerson() {Person p1 = new Person("小黑說Java", 18, "北京市西城區(qū)");Person p2 = new Person("小白說Java", 28, "北京市東城區(qū)");System.out.println(p1);System.out.println(p2);System.out.println(p1.equals(p2)); }

假設(shè)有一些場(chǎng)景我們只需要對(duì)Person的name和age屬性進(jìn)行打印,在有record之后將會(huì)變得非常容易。

public static void testPerson() {Person p1 = new Person("小黑說Java", 18, "北京市西城區(qū)");Person p2 = new Person("小白說Java", 28, "北京市東城區(qū)");// 使用record定義record PersonRecord(String name,int age){}PersonRecord p1Record = new PersonRecord(p1.getName(), p1.getAge());PersonRecord p2Record = new PersonRecord(p2.getName(), p2.getAge());System.out.println(p1Record);System.out.println(p2Record); }

record也可以單獨(dú)定義作為一個(gè)文件定義,但是因?yàn)镽ecord的使用非常緊湊,所以可以直接在需要使用的地方直接定義。

record同樣也有構(gòu)造方法,可以在構(gòu)造方法中對(duì)數(shù)據(jù)進(jìn)行一些驗(yàn)證操作。

public static void testPerson() {Person p1 = new Person("小黑說Java", 18, "北京市西城區(qū)");Person p2 = new Person(null, 28, "北京市東城區(qū)");record PersonRecord(String name, int age) {// 構(gòu)造方法PersonRecord {System.out.println("name " + name + " age " + age);if (name == null) {throw new IllegalArgumentException("姓名不能為空");}}}PersonRecord p1Record = new PersonRecord(p1.getName(), p1.getAge());PersonRecord p2Record = new PersonRecord(p2.getName(), p2.getAge()); }

?密封類 sealed class

密封類可以讓我們更好的控制哪些類可以對(duì)我定義的類進(jìn)行擴(kuò)展。密封類可能對(duì)于框架或中間件的開發(fā)者更有用。在這之前一個(gè)類要么是可以被extends的,要么是final的,只有這兩種選項(xiàng)。

密封類可以控制有哪些類可以對(duì)超類進(jìn)行繼承,在Java 17之前如果我們需要控制哪些類可以繼承,可以通過改變類的訪問級(jí)別,比如去掉類的public,訪問級(jí)別為默認(rèn)。比如我們?cè)赾om.heiz.java11包中定義了如下的三個(gè)類:

package com.heiz.java11; public abstract class Furit { } public class Apple extends Furit { } public class Pear extends Furit { }

那么我們可以在另一個(gè)包c(diǎn)om.heiz123.java11中寫如下的代碼:

private static void test() {Apple apple = new Apple();Pear pear = new Pear();Fruit fruit = apple;class Avocado extends Fruit {}; }

既可以定義Apple,Pear,也可以將apple實(shí)例賦值給Fruit,并且可以對(duì)Fruit進(jìn)行繼承。

如果我們不想讓Fruit在com.heiz.java11包以外被擴(kuò)展,在Java11版本中只能改變?cè)L問權(quán)限,去掉class的public修飾符。這樣雖然可以控制被被繼承,但是也會(huì)導(dǎo)致Fruit fruit = apple;也編譯失敗;在Java 17中通過密封類可以解決這個(gè)問題。

package com.heiz.java17;public abstract sealed class Furit permits Apple,Pear { } public non-sealed class Apple extends Furit { } public final class Pear extends Furit {}

在定義Furit時(shí)通過關(guān)鍵字sealed聲明為密封類,通過permits可以指定Apple,Pear類可以進(jìn)行繼承擴(kuò)展。

子類需要指明它是final,non-sealed或sealed的。父類不能控制子類是否可以被繼承。

private static void test() {Apple apple = new Apple();Pear pear = new Pear();// 可以將apple賦值給FruitFruit fruit = apple;// 只能繼承Apple,不能繼承Furitclass Avocado extends Apple {}; }

instanceof模式匹配?

通常我們使用instanceof時(shí),一般發(fā)生在需要對(duì)一個(gè)變量的類型進(jìn)行判斷,如果符合指定的類型,則強(qiáng)制類型轉(zhuǎn)換為一個(gè)新變量。

private static void oldStyle(Object o) {if (o instanceof Furit) {Furit furit = (GrapeClass) o;System.out.println("This furit is :" + furit.getName);} }

在使用instanceof的模式匹配后,上面的代碼可進(jìn)行簡寫。

private static void oldStyle(Object o) {if (o instanceof Furit furit) {System.out.println("This furit is :" + furit.getName);} }

可以將類型轉(zhuǎn)換和變量聲明都在if中處理。同時(shí),可以直接在if中使用這個(gè)變量。

private static void oldStyle(Object o) {if (o instanceof Furit furit && furit.getColor()==Color.RED) {System.out.println("This furit is :" + furit.getName);} }

因?yàn)橹挥挟?dāng)instanceof的結(jié)果為true時(shí),才會(huì)定義變量furit,所以這里可以使用&&,但是改為||就會(huì)編譯報(bào)錯(cuò)。

Helpful NullPointerExceptions

Helpful NullPointerExceptions可以在我們遇到NPE時(shí)節(jié)省一些分析時(shí)間。如下的代碼會(huì)導(dǎo)致一個(gè)NPE。

public static void main(String[] args) {Person p = new Person();String cityName = p.getAddress().getCity().getName(); }

在Java 11中,輸出將顯示NullPointerException發(fā)生的行號(hào),但不知道哪個(gè)方法調(diào)用時(shí)產(chǎn)生的null,必須通過調(diào)試的方式找到。

Exception in thread "main" java.lang.NullPointerExceptionat com.heiz.java17.HelpfulNullPointerExceptionsDemo.main(HelpfulNullPointerExceptionsDemo.java:13)

在Java 17中,則會(huì)準(zhǔn)確顯示發(fā)生NPE的精確位置。

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.heiz.java17.Address.getCity()" because the return value of "com.heiz.java17.Person.getAddress()" is nullat com.heiz.java17.HelpfulNullPointerExceptionsDemo.main(HelpfulNullPointerExc

日期周期格式化?

在Java 17中添加了一個(gè)新的模式B,用于格式化DateTime,它根據(jù)Unicode標(biāo)準(zhǔn)指示一天時(shí)間段。

使用默認(rèn)的英語語言環(huán)境,打印一天的幾個(gè)時(shí)刻:

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("B"); System.out.println(dtf.format(LocalTime.of(8, 0))); System.out.println(dtf.format(LocalTime.of(13, 0))); System.out.println(dtf.format(LocalTime.of(20, 0))); System.out.println(dtf.format(LocalTime.of(23, 0))); System.out.println(dtf.format(LocalTime.of(0, 0)));

輸出結(jié)果:

in the morning in the afternoon in the evening at night midnight

如果是中文語言環(huán)境,則輸出結(jié)果為:

上午 下午 晚上 晚上 午夜

可見咱們的晚上是包括英美國家的evening和night的。

精簡數(shù)字格式化支持

在NumberFormat中添加了一個(gè)工廠方法,可以根據(jù)Unicode標(biāo)準(zhǔn)以緊湊的、人類可讀的形式格式化數(shù)字。

SHORT格式如下所示:

NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.SHORT); System.out.println(fmt.format(1000)); System.out.println(fmt.format(100000)); System.out.println(fmt.format(1000000));

輸出格式為:

1K 100K 1M

LONG格式如下所示:

fmt = NumberFormat.getCompactNumberInstance(Locale.ENGLISH, NumberFormat.Style.LONG); System.out.println(fmt.format(1000)); System.out.println(fmt.format(100000)); System.out.println(fmt.format(1000000));

輸出結(jié)果為:

1 thousand 100 thousand 1 million

Stream.toList()?

如果需要將Stream轉(zhuǎn)換成List,需要通過調(diào)用collect方法使用Collectors.toList(),代碼非常冗長。

private static void oldStyle() {Stream<String> stringStream = Stream.of("a", "b", "c");List<String> stringList = stringStream.collect(Collectors.toList());for(String s : stringList) {System.out.println(s);} }

在Java 17中將會(huì)變得簡單,可以直接調(diào)用toList()。

private static void streamToList() {Stream<String> stringStream = Stream.of("a", "b", "c");List<String> stringList = stringStream.toList();for(String s : stringList) {System.out.println(s);} }

總結(jié)

以上是生活随笔為你收集整理的Java 17的一些新特性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。