Java?17新特性詳細(xì)講解與代碼實(shí)例
前言
Java 17是Java SE 17的開(kāi)源參考實(shí)現(xiàn),于2021年9月14日正式發(fā)布,是Java 11以來(lái)的又一個(gè)長(zhǎng)期支持(LTS)版本。Java 17中有一些新的特性和改進(jìn),本文將對(duì)它們進(jìn)行簡(jiǎn)要的介紹和示例。
密封類
密封類和接口限制了哪些其他類或接口可以擴(kuò)展或?qū)崿F(xiàn)它們,增強(qiáng)了封裝性和可維護(hù)性。密封類由JEP 360并在JDK 15中作為預(yù)覽功能交付。它們?cè)俅伪惶岢?,并進(jìn)行了改進(jìn),由JEP 397并在JDK 16中作為預(yù)覽功能提供?,F(xiàn)在,在JDK 17中,密封類正在最終確定,與JDK 16沒(méi)有任何更改。
要定義一個(gè)密封類或接口,需要使用sealed修飾符,并且在聲明中指定允許擴(kuò)展或?qū)崿F(xiàn)它的其他類或接口。這些類或接口被稱為子類或子接口。子類或子接口可以使用final修飾符來(lái)表示它們不能被進(jìn)一步擴(kuò)展或?qū)崿F(xiàn),或者使用sealed修飾符來(lái)表示它們也是密封的,并且需要指定它們的子類或子接口,或者使用non-sealed修飾符來(lái)表示它們不是密封的,并且可以被任意的類或接口擴(kuò)展或?qū)崿F(xiàn)。
例如,我們可以定義一個(gè)密封的形狀接口Shape,并且指定它只能被Circle、Rectangle和Triangle這三個(gè)類實(shí)現(xiàn):
public sealed interface Shape permits Circle, Rectangle, Triangle {
double area();
}然后,我們可以定義這三個(gè)類,并且分別使用final、sealed和non-sealed修飾符:
public final class Circle implements Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
public double area() {
return Math.PI * radius * radius;
}
}
public sealed class Rectangle implements Shape permits Square {
private final double length;
private final double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double area() {
return length * width;
}
}
public non-sealed class Triangle implements Shape {
private final double base;
private final double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
public double area() {
return base * height / 2;
}
}注意,Square是Rectangle的子類,并且也是密封的:
public final class Square extends Rectangle {
public Square(double side) {
super(side, side);
}
}這樣,我們就可以保證Shape接口只能被這四個(gè)類實(shí)現(xiàn),而不會(huì)有其他的可能性。我們也可以使用模式匹配來(lái)對(duì)不同的形狀進(jìn)行操作,例如:
public static void printArea(Shape shape) {
if (shape instanceof Circle c) {
System.out.println("The area of the circle is " + c.area());
} else if (shape instanceof Rectangle r) {
System.out.println("The area of the rectangle is " + r.area());
} else if (shape instanceof Triangle t) {
System.out.println("The area of the triangle is " + t.area());
}
}switch表達(dá)式
switch表達(dá)式允許switch有返回值,并且可以直接作為結(jié)果賦值給一個(gè)變量,簡(jiǎn)化了多分支的邏輯。switch表達(dá)式由JEP 325并在JDK 12中作為預(yù)覽功能交付。它們?cè)俅伪惶岢觯⑦M(jìn)行了改進(jìn),由JEP 354并在JDK 13中作為預(yù)覽功能提供。它們?cè)俅伪惶岢?,并進(jìn)行了改進(jìn),由JEP 361并在JDK 14中作為預(yù)覽功能提供?,F(xiàn)在,在JDK 17中,switch表達(dá)式正在最終確定,與JDK 14沒(méi)有任何更改。
要定義一個(gè)switch表達(dá)式,需要使用->符號(hào)來(lái)表示每個(gè)分支的結(jié)果,并且在表達(dá)式的末尾加上一個(gè)分號(hào)。例如,我們可以定義一個(gè)根據(jù)月份返回季節(jié)的switch表達(dá)式:
public static String getSeason(int month) {
return switch (month) {
case 12, 1, 2 -> "Winter";
case 3, 4, 5 -> "Spring";
case 6, 7, 8 -> "Summer";
case 9, 10, 11 -> "Autumn";
default -> "Unknown";
};
}注意,我們可以使用逗號(hào)來(lái)分隔多個(gè)匹配值,也可以使用default來(lái)表示其他情況。我們也可以使用yield關(guān)鍵字來(lái)返回一個(gè)值,這在需要在返回之前進(jìn)行一些操作的情況下很有用,例如:
public static String getSeason(int month) {
return switch (month) {
case 12, 1, 2 -> {
System.out.println("It's cold!");
yield "Winter";
}
case 3, 4, 5 -> {
System.out.println("It's warm!");
yield "Spring";
}
case 6, 7, 8 -> {
System.out.println("It's hot!");
yield "Summer";
}
case 9, 10, 11 -> {
System.out.println("It's cool!");
yield "Autumn";
}
default -> {
System.out.println("It's unknown!");
yield "Unknown";
}
};
}文本塊
文本塊允許使用三個(gè)雙引號(hào)來(lái)定義一個(gè)多行的字符串,避免了轉(zhuǎn)義和拼接的麻煩。文本塊由JEP
355并在JDK 13中作為預(yù)覽功能交付。它們?cè)俅伪惶岢?,并進(jìn)行了改進(jìn),由JEP 368并在JDK 14中作為預(yù)覽功能提供。它們?cè)俅伪惶岢觯⑦M(jìn)行了改進(jìn),由JEP 378并在JDK 15中作為預(yù)覽功能提供?,F(xiàn)在,在JDK 17中,文本塊正在最終確定,與JDK 15沒(méi)有任何更改。
要定義一個(gè)文本塊,需要使用三個(gè)雙引號(hào)"""來(lái)開(kāi)始和結(jié)束,并且結(jié)束的三個(gè)雙引號(hào)不能和開(kāi)始的在同一行。例如,我們可以定義一個(gè)包含JSON數(shù)據(jù)的文本塊:
public static String getJson() {
return """
{
"name": "Java",
"version": 17,
"features": [
"sealed classes",
"switch expressions",
"text blocks"
]
}
""";
}注意,文本塊中的換行符會(huì)被保留,而不需要使用\n來(lái)表示。我們也可以使用\來(lái)表示忽略換行符,或者使用\s來(lái)表示一個(gè)空格。例如:
public static String getGreeting() {
return """
Hello,\
World!
""";
}
public static String getPoem() {
return """
Twinkle, twinkle, little star,\s
How I wonder what you are.\s
Up above the world so high,\s
Like a diamond in the sky.
""";
}模式匹配
模式匹配允許在instanceof和switch中使用模式來(lái)測(cè)試表達(dá)式的類型和結(jié)構(gòu),提高了代碼的可讀性和靈活性。模式匹配由JEP 305并在JDK 14中作為預(yù)覽功能交付。它們?cè)俅伪惶岢?,并進(jìn)行了改進(jìn),由JEP 375并在JDK 15中作為預(yù)覽功能提供。它們?cè)俅伪惶岢?,并進(jìn)行了改進(jìn),由JEP 394并在JDK 16中作為預(yù)覽功能提供?,F(xiàn)在,在JDK 17中,模式匹配正在最終確定,與JDK 16沒(méi)有任何更改。
要使用模式匹配,需要使用instanceof或switch關(guān)鍵字,并且在測(cè)試的類型后面加上一個(gè)變量名,用于綁定匹配的值。例如,我們可以使用模式匹配來(lái)判斷一個(gè)對(duì)象是否是字符串,并且獲取它的長(zhǎng)度:
public static void printLength(Object obj) {
if (obj instanceof String s) {
System.out.println("The length of the string is " + s.length());
} else {
System.out.println("The object is not a string");
}
}注意,我們不需要再進(jìn)行強(qiáng)制類型轉(zhuǎn)換,因?yàn)樽兞?code>s已經(jīng)被綁定為字符串類型。我們也可以使用模式匹配來(lái)判斷一個(gè)對(duì)象是否是密封類的子類,并且獲取它的屬性:
public static void printShape(Shape shape) {
switch (shape) {
case Circle c -> System.out.println("The radius of the circle is " + c.radius());
case Rectangle r -> System.out.println("The area of the rectangle is " + r.area());
case Triangle t -> System.out.println("The base of the triangle is " + t.base());
default -> System.out.println("Unknown shape");
}
}注意,我們不需要再進(jìn)行類型檢查或者類型轉(zhuǎn)換,因?yàn)樽兞?code>c、r和t已經(jīng)被綁定為相應(yīng)的類型。
增強(qiáng)型偽隨機(jī)數(shù)生成器
增強(qiáng)型偽隨機(jī)數(shù)生成器為PRNG提供了新的接口類型和實(shí)現(xiàn),包括可跳轉(zhuǎn)的PRNG和一類額外的可拆分PRNG算法(LXM)。增強(qiáng)型偽隨機(jī)數(shù)生成器由JEP 356并在JDK 17中作為正式功能提供。
要使用增強(qiáng)型偽隨機(jī)數(shù)生成器,需要使用java.util.random包中的新的接口和類。例如,我們可以使用RandomGenerator接口來(lái)獲取一個(gè)PRNG的實(shí)例,并且使用它來(lái)生成各種類型的隨機(jī)數(shù):
public static void generateRandomNumbers() {
RandomGenerator random = RandomGenerator.getDefault();
System.out.println("A random boolean: " + random.nextBoolean());
System.out.println("A random int: " + random.nextInt());
System.out.println("A random long: " + random.nextLong());
System.out.println("A random float: " + random.nextFloat());
System.out.println("A random double: " + random.nextDouble());
}注意,RandomGenerator接口提供了很多方便的方法來(lái)生成不同范圍和分布的隨機(jī)數(shù),例如nextInt(int bound)、nextLong(long bound)、nextGaussian()等。我們也可以使用RandomGeneratorFactory類來(lái)獲取不同的PRNG算法的實(shí)例,例如:
public static void useDifferentAlgorithms() {
RandomGenerator random1 = RandomGeneratorFactory.of("L32X64MixRandom");
RandomGenerator random2 = RandomGeneratorFactory.of("L64X128MixRandom");
RandomGenerator random3 = RandomGeneratorFactory.of("L128X256MixRandom");
System.out.println("Using L32X64MixRandom: " + random1.nextInt());
System.out.println("Using L64X128MixRandom: " + random2.nextInt());
System.out.println("Using L128X256MixRandom: " + random3.nextInt());
}注意,RandomGeneratorFactory類提供了很多方法來(lái)獲取或者查詢不同的PRNG算法,例如of(String name)、all()、preferred()等。我們也可以使用JumpableRandomGenerator接口或者SplittableRandomGenerator接口來(lái)獲取一個(gè)可跳轉(zhuǎn)或者可拆分的PRNG實(shí)例,并且使用它們來(lái)生成不同的子生成器,例如:
public static void useJumpableOrSplittableGenerators() {
JumpableRandomGenerator jumpable = RandomGeneratorFactory.jumpable();
SplittableRandomGenerator splittable = RandomGeneratorFactory.splittable();
System.out.println("Using the original jumpable generator: " + jumpable.nextInt());
System.out.println("Using the original splittable generator: " + splittable.nextInt());
JumpableRandomGenerator jumped = jumpable.jump();
SplittableRandomGenerator splitted = splittable.split();
System.out.println("Using the jumped generator: " + jumped.nextInt());
System.out.println("Using the splitted generator: " + splitted.nextInt());
}注意,JumpableRandomGenerator接口和SplittableRandomGenerator接口都繼承自RandomGenerator接口,并且提供了額外的方法來(lái)生成子生成器,例如jump()、split()等。這些子生成器可以用于并行計(jì)算或者其他場(chǎng)景。
新的macOS渲染管道
新的macOS渲染管道使用Apple Metal加速渲染API來(lái)替代被Apple棄用的OpenGL API。新的macOS渲染管道由JEP 382并在JDK 17中作為正式功能提供。
要使用新的macOS渲染管道,需要在運(yùn)行Java程序時(shí)設(shè)置系統(tǒng)屬性:
-Dsun.java2d.metal=true
這樣,Java 2D API和Swing API用于渲染的Java 2D API就可以使用Metal API來(lái)加速渲染。這對(duì)于Java程序是透明的,因?yàn)檫@是內(nèi)部實(shí)現(xiàn)的區(qū)別,對(duì)Java API沒(méi)有影響。Metal管道需要macOS 10.14.x或更高版本。在早期版本上設(shè)置它的嘗試將被忽略。
總結(jié)
到此這篇關(guān)于Java 17新特性的文章就介紹到這了,更多相關(guān)Java17新特性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis使用resultMap如何解決列名和屬性名不一致
這篇文章主要介紹了MyBatis使用resultMap如何解決列名和屬性名不一致的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
Java中關(guān)于MouseWheelListener的鼠標(biāo)滾輪事件詳解
這篇文章主要介紹了Java中關(guān)于MouseWheelListener的鼠標(biāo)滾輪事件詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot整合Mybatis-plus案例及用法實(shí)例
mybatis-plus是一個(gè) Mybatis 的增強(qiáng)工具,在 Mybatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開(kāi)發(fā)、提高效率而生,下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合Mybatis-plus案例及用法實(shí)例的相關(guān)資料,需要的朋友可以參考下2022-11-11
mybatis-plus分表實(shí)現(xiàn)案例(附示例代碼)
MyBatis-Plus是一個(gè)MyBatis的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開(kāi)發(fā)、提高效率而生,這篇文章主要介紹了mybatis-plus分表實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-12-12
Spring Boot外部化配置實(shí)戰(zhàn)解析
這篇文章主要介紹了Spring Boot外部化配置實(shí)戰(zhàn)解析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-06-06

