JDK17 新特性 Preview 1

2023年,越来越多的开发者和企业将会迁移到 JDK 17,我们学习 Java 也应该跟上时代的步伐。随着 JDK 17 的发布,Java 语言和 JVM 将迎来一些新的特性,本文将介绍 JDK 17 的新特性。

switch 增强

JDk 17 为 switch 增加了一些新的特性,包括:

  • switch 表达式

  • switch 语句中的 yield 语句

  • switch 语句中的模式匹配

switch 表达式

Before JDK 17:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SwitchDemo {
public static void main(String[] args) {
int num = 1;
switch (num) {
case 1:
System.out.println("num is 1");
break;
case 2:
System.out.println("num is 2");
break;
default:
System.out.println("num is not 1 or 2");
}
}
}

After JDK 17:

1
2
3
4
5
6
7
8
9
10
public class SwitchDemo {
public static void main(String[] args) {
int num = 1;
switch (num) {
case 1 -> System.out.println("num is 1");
case 2 -> System.out.println("num is 2");
default -> System.out.println("num is not 1 or 2");
}
}
}

可以看出,原先的 switch 语句中,每个 case 都需要加上 break 语句,否则会出现穿透现象。而在 JDK 17 中,switch 语句中的 case 可以直接使用 -> 来代替,这样就不需要加上 break 语句了。

switch 语句中的 yield 语句

Before JDK 17:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
String name = "徐庶";
String country;
switch (name) {
case "诸葛亮":
country = "蜀国";
break;
case "司马懿":
country = "魏国";
break;
case "徐庶":
country = "吴国";
break;
default:
country = "未知";
break;
}
System.out.println(name + "是" + country + "人");

After JDK 17:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
String name = "徐庶";
String country = switch (name) {
case "诸葛亮", "庞统" -> {
yield "蜀国";
}
case "司马懿", "郭嘉" -> {
yield "魏国";
}
case "徐庶", "周瑜" -> {
yield "吴国";
}
default -> {
yield "未知";
}
};
System.out.println(name + "是" + country + "人");

在 JDK 17 中,switch 语句中的 case 可以直接使用 yield 语句来代替 break 语句。而且 yield 语句可以返回一个值,这样就不需要定义一个变量来接收了。在原来的 switch 语句中,每个 case 都需要加上 break 语句,否则会出现穿透现象。而在 JDK 17 中,switch 语句中的 case 可以直接使用 yield 语句来代替,这样就不需要加上 break 语句了。

switch 语句中的模式匹配

JDK 17:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private String getCountry(Object obj) {
return switch (obj) {
case String s -> {
switch (s) {
case "诸葛亮", "庞统" -> {
yield "蜀国";
}
case "司马懿", "郭嘉" -> {
yield "魏国";
}
case "徐庶", "周瑜" -> {
yield "吴国";
}
default -> {
yield "未知";
}
}
}
case Integer i -> {
switch (i) {
case 1, 2, 3 -> {
yield "蜀国";
}
case 4, 5, 6 -> {
yield "魏国";
}
case 7, 8, 9 -> {
yield "吴国";
}
default -> {
yield "未知";
}
}
}
default -> {
yield "未知";
}
};
}

这是 JDK 17 中的新特性,可以在 switch 语句中使用模式匹配,这样就不需要使用 instanceof 来判断对象的类型了。

拼接字符串增强

JDK 17 为 String 类增加了一个新的方法,用于拼接字符串。

Before JDK 17:

1
2
3
String str = "Hello";
str = str + " World";
System.out.println(str);

After JDK 17:

1
2
3
String str = "Hello";
str = str.concat(" World");
System.out.println(str);

使用 \ 来拼接字符串,使用 \s 来表示空格,使用 \n 来表示换行。

Before JDK 17:

1
2
3
4
5
6
String html = "<html>\n" +
"<body>\n" +
" <h1>Hello World</h1>\n" +
"</body>\n" +
"</html>";
System.out.println(html);

原先每一行都需要加上 \n 来表示换行,这样的代码可读性很差。

After JDK 17:

1
2
3
4
5
6
7
8
String html = """
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
""";
System.out.println(html);

instanceof 模式匹配

JDK 17 为 instanceof 增加了模式匹配的功能。

Before JDK 17:

1
2
3
4
5
6
7
8
9
public class InstanceofDemo {
public static void main(String[] args) {
Object obj = "Hello";
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.toUpperCase());
}
}
}

After JDK 17:

1
2
3
4
5
6
7
8
public class InstanceofDemo {
public static void main(String[] args) {
Object obj = "Hello";
if (obj instanceof String str) {
System.out.println(str.toUpperCase());
}
}
}

在原先的代码中,我们需要先判断 obj 是否是 String 类型,如果是,我们需要将 obj 强转为 String 类型,然后才能调用 String 类型的方法。在 JDK 17 中,我们可以直接在 if 语句中使用 instanceof,如果 obj 是 String 类型,我们可以直接使用 str 变量,而不需要强转。

密封类

JDK 17 为类增加了一个新的修饰符 sealed,用于修饰类,接口和枚举。对于称之为密封类,更多是为了限制子类的继承,防止子类变成父类。

Before JDK 17:

1
2
3
4
5
6
7
8
9
10
11
12
public class SealedClassDemo {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();
}
}

class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}

After JDK 17:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SealedClassDemo {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();
}
}

sealed class Animal permits Cat, Dog {
public void eat() {
System.out.println("动物吃东西");
}
}

final class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}

final class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}

在 JDK 17 中,我们使用 sealed 修饰 Animal 类,使用 permits 关键字来指定 Animal 类可以被哪些类继承,这里我们指定 Cat 和 Dog 类可以继承 Animal 类。在 Cat 和 Dog 类中,我们使用 final 修饰类,防止子类继续继承 Cat 和 Dog 类。除了final 修饰类,我们还可以使用 sealed 修饰类,这样的类只能被指定的类继承,不能被其他类继承。

这种方式有很多优点

  • 限制子类的继承,防止子类变成父类

  • 限制子类的个数,防止子类过多

  • 限制子类的继承层级,防止继承层级过多