JDK 21 与Springboot3的新特性
JDK 21 是 Java 的最新长期支持(LTS)版本,引入了许多新特性,主要包括:
-
Record:简化不可变类的定义。
-
Switch 表达式:增强 Switch 语句的功能。
-
文本块:简化多行字符串的定义。
-
var 关键字:局部变量类型推断。
-
密闭类(Sealed Classes):限制类的继承。
-
模式匹配:简化 instanceof 和类型转换。
1.Record类
Record 是 JDK 14 引入的特性,用于简化不可变类的定义。它自动生成 equals()
、hashCode()
、toString()
等方法,类似于 Lombok 的 @Data
注解。Record默认创建不可变的数据结构,字段默认为final
,因此非常适合数据完整性至关重要的使用场景 示例
public record User(String name, int age) {}
等同于以下代码:
public final class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String name() { return name; }
public int age() { return age; }
@Override
public boolean equals(Object o) { ... }
@Override
public int hashCode() { ... }
@Override
public String toString() { ... }
}
定义一个简单的 Record:
public record Point(int x, int y) {}
使用 Record:
Point p = new Point(10, 20);
System.out.println(p.x()); // 输出 10
System.out.println(p.y()); // 输出 20
Record - 实例方法与静态方法
public record Point(int x, int y) {
// 实例方法
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
// 静态方法
public static Point origin() {
return new Point(0, 0);
}
}
//调用方法
public class Main {
public static void main(String[] args) {
// 创建一个Point实例
Point point = new Point(3, 4);
// 调用实例方法distanceFromOrigin()
double distance = point.distanceFromOrigin();
System.out.println("Distance from origin: " + distance);
// 调用静态方法origin()
Point origin = Point.origin();
System.out.println("Origin: (" + origin.x() + ", " + origin.y() + ")");
}
}
Record - 三种构造方法
Record 支持三种构造方法:
-
规范构造方法:默认生成。
-
紧凑构造方法:用于参数验证。
Record实现接口
Java Record可以像普通类一样实现接口,并重写接口中的方法。
// 定义一个接口
public interface PrintInterface {
void print();
}
// 定义一个Record并实现接口
public record ProductRecord(Integer id, String name, Integer qty) implements PrintInterface {
@Override
public void print() {
StringJoiner joiner = new StringJoiner("-");
joiner.add(id.toString()).add(name).add(qty.toString());
System.out.println("商品信息 = " + joiner);
}
}
// 测试
public class Main {
public static void main(String[] args) {
ProductRecord productRecord = new ProductRecord(1001, "手机", 200);
productRecord.print(); // 输出:商品信息 = 1001-手机-200
}
}
Record的继承
Java Record是隐式final
的,不能被继承。这意味着你不能创建一个继承自Record的子类。Record的所有字段和方法在编译时已经固定,无法通过继承来扩展。
示例:
record Person(String name, int age) {} // 基础Record
// 以下代码会导致编译错误,Record不能被继承
// record Student(String name, int age, String school) extends Person {}
Record的灵活性
尽管Record不能被继承,但它可以实现多个接口。这使得Record在多态场景下具有很好的灵活性。示例:
public interface PrintInterface {
void print();
}
public interface LogInterface {
void log();
}
public record ProductRecord(Integer id, String name, Integer qty) implements PrintInterface, LogInterface {
@Override
public void print() {
System.out.println("商品信息 = " + id + "-" + name + "-" + qty);
}
@Override
public void log() {
System.out.println("日志记录:商品ID = " + id);
}
}
public class Main {
public static void main(String[] args) {
ProductRecord productRecord = new ProductRecord(1001, "手机", 200);
productRecord.print(); // 输出商品信息
productRecord.log(); // 输出日志记录
}
}
Record 与 Lombok 对比
特性 | Record | Lombok (@Data) |
---|---|---|
不可变性 | 是 | 需要手动设置 final |
代码简洁性 | 更简洁 | 需要注解 |
功能扩展 | 有限 | 更灵活 |
兼容性 | JDK 14+ | 兼容所有 JDK 版本 |
Record 总结
-
Java Record可以实现接口,并重写接口中的方法,这使得Record可以参与多态。
-
Java Record不能被继承,因为它是隐式
final
的。 -
Record通过实现接口可以增强其灵活性和可用性。
如果你需要更复杂的继承关系或可变的数据结构,建议使用普通类而不是Record。
2. Switch 箭头表达式
JDK 12 引入了 Switch 表达式,JDK 13 引入了 yield
关键字。
String day = "MON";
String result = switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> "Weekday";
case "SAT", "SUN" -> "Weekend";
default -> throw new IllegalArgumentException("Invalid day");
};
Switch yield
yield
用于在 Switch 表达式中返回值:
1. yield
的作用
yield
关键字用于从switch
代码块中返回一个值。它类似于函数中的return
语句,但只能在switch
代码块中使用。使用yield
可以将一个值传递给switch
表达式的调用者。
yield
可以用于更复杂的逻辑中,例如在代码块中进行计算后返回结果。
2.yield
与return
的区别
-
yield
:-
只能在
switch
代码块中使用。 -
用于从
switch
代码块中返回一个值。 -
使得
switch
表达式可以作为一个整体返回一个值。
-
-
return
:-
可以在任何方法中使用。
-
用于从方法中返回一个值。
-
不能在
switch
代码块中直接使用(除非switch
代码块在方法中)。
-
3.switch
表达式的语法
switch
表达式可以包含多个case
语句,每个case
语句可以使用->
或{}
语法。如果使用{}
语法,则可以在代码块中使用yield
返回值。
String day = "MON";
String result = switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> {
System.out.println("It's a weekday");
yield "Weekday";
}
case "SAT", "SUN" -> {
System.out.println("It's a weekend");
yield "Weekend";
}
default -> throw new IllegalArgumentException("Invalid day");
};
Switch 代码块和 yield
Switch 表达式支持代码块:
String day = "MON";
String result = switch (day) {
case "MON", "TUE", "WED", "THU", "FRI" -> {
System.out.println("It's a weekday");
yield "Weekday";
}
case "SAT", "SUN" -> {
System.out.println("It's a weekend");
yield "Weekend";
}
default -> throw new IllegalArgumentException("Invalid day");
};
Switch Record
Switch 表达式支持 Record 模式匹配:
record Point(int x, int y) {}
static String checkPoint(Object obj) {
return switch (obj) {
case Point(int x, int y) -> "Point: (" + x + ", " + y + ")";
default -> "Unknown";
};
}
3.文本块 - 创建文本块
文本块(Text Blocks)是 JDK 13 引入的特性,用于简化多行字符串的定义。示例
String json = """
{
"name": "John",
"age": 30
}
""";
文本块 - 方法
文本块支持字符串方法:
String text = """
Hello,
World!
""";
System.out.println(text.stripIndent()); // 去除缩进
4.var 关键字
var
是 JDK 10 引入的关键字,用于局部变量类型推断。
var list = new ArrayList<String>();
list.add("Hello");
list.add("World");
var i = 0;
var i = "asdasd";
Sealed 密闭类
密闭类(Sealed Classes)是 JDK 15 引入的特性,用于限制类的继承。
密封接口使用sealed
关键字定义,并通过permits
关键字指定允许实现该类了的具体类或接口。你的代码中已经正确地使用了Circle
和 Square
。
//Shape permits Circle, Square 这个表示这个类只能被 Circle 和 Square 继承(这主要用与框架和底层)
public sealed class Shape permits Circle, Square {
// 父类
}
public final class Circle extends Shape {
// 子类
}
public final class Square extends Shape {
// 子类
}
Sealed 密闭接口
密闭接口(Sealed Interfaces)与密闭类类似,用于限制接口的实现。
密封接口使用sealed
关键字定义,并通过permits
关键字指定了允许实现该接口的具体类或接口。你的代码中已经正确地使用了sealed
和permits
。
//Shape permits Circle, Square 这个表示 这个接口只能被Circle 和 Square 实现 (这主要用与框架和底层)
public sealed interface Animal permits Dog, Cat {
// 父接口
}
public final class Dog implements Animal {
// 实现类
}
public final class Cat implements Animal {
// 实现类
}