前置知识: Java

Java新特性

3 minIntermediate2026/6/14

Java 17-21新特性

概述

Java 从 17 到 21 版本引入了大量现代语言特性,包括 Record 、密封、模式匹配、虚拟线程等。这些特性使 Java 代码更简洁、更安全、更易表达业务意。Java 17 是当前的 LTS 版本,Java 21 是最新的 LTS 版本,企业应优先升级到这两个版本。

基础概念

版本路线

版本关键特性
Java 17LTS密封、模式匹配预览、强封装
Java 19特性虚拟线程预览、结构化并发预览
Java 20特性作用域值预览、记录模式预览
Java 21LTS虚拟线程正式、模式匹配正式、记录模式正式

特性

  • 语法增强:Record、密封、模式匹配、switch 表达式
  • 并发改进:虚拟线程、结构化并发、作用域
  • API 增强:新 String 方法、新集合工厂方法、新 IO 方法

快速上手

Record (Java 16+)

// Record 自动生成构造器、getter、equals、hashCode、toString
public record Point(int x, int y) {
    // 紧凑构造器:用于参数验证
    public Point {
        if (x < 0 || y < 0) {
            throw new IllegalArgumentException("坐标不能为负数");
        }
    }

    // 自定义方法
    public double distanceTo(Point other) {
        return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(y - other.y, 2));
    }
}

var p = new Point(3, 4);
p.x();   // 3(注意:没有 getX(),直接用 x())
p.y();   // 4

密封(Java 17+)

// 密封类限制继承层级,编译器可穷举所有子类
public sealed interface Shape permits Circle, Rectangle, Triangle {}

// 子类必须是 final、sealed 或 non-sealed
public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public non-sealed class Triangle implements Shape {
    private final double a, b, c;
    public Triangle(double a, double b, double c) { this.a = a; this.b = b; this.c = c; }
}

模式匹配 instanceof(Java 16+)

// 旧写法
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// 新写法:同时判断和绑定变量
if (obj instanceof String s && s.length() > 5) {
    System.out.println(s.toUpperCase()); // s 已绑定,直接使用
}

// 与逻辑运算组合
if (obj instanceof Integer i && i > 0) {
    System.out.println("正整数: " + i);
}

详细用法

Switch 模式匹配(Java 21)

// 使用模式匹配的 switch
static String formatShape(Shape shape) {
    return switch (shape) {
        case Circle(var r) when r > 0   -> "圆形,半径: " + r;
        case Circle(var r)              -> "退化的圆";
        case Rectangle(var w, var h)    -> "矩形,面积: " + (w * h);
        case Triangle t                 -> "三角形";
    };
}

// 穷举密封类型的所有分支,无需 default
static double area(Shape shape) {
    return switch (shape) {
        case Circle(var r)         -> Math.PI * r * r;
        case Rectangle(var w, var h) -> w * h;
        case Triangle t            -> heronFormula(t);
    };
}

文本块(Java 15+)

// 多行字符串,适合 SQL、JSON、HTML
String query = """
    SELECT u.name, u.email
    FROM users u
    WHERE u.age > %d
    ORDER BY u.name
    """.formatted(18);

String json = """
    {
        "name": "%s",
        "age": %d
    }
    """.formatted("张三", 25);

新增 String 方法

// Java 11+ 新增方法
"  hello  ".strip();          // "hello"(Unicode 感知的 trim)
"  hello  ".stripLeading();   // "hello  "
"  hello  ".stripTrailing();  // "  hello"
"hello".repeat(3);            // "hellohellohello"
"hello".isBlank();            // false
"".isBlank();                 // true
"line1\nline2\nline3".lines().count(); // 3

// Java 12+ 新增方法
"hello".indent(4);            // "    hello\n"
"  hello  ".stripIndent();    // 去除公共缩进

新增集合工厂方法

// Java 9+ 不可变集合工厂
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("a", 1, "b", 2);
Map<String, Integer> map2 = Map.ofEntries(
    Map.entry("x", 10),
    Map.entry("y", 20)
);

// 注意:返回的是不可变集合,修改会抛出 UnsupportedOperationException

常见场景

Record 替代 DTO

// 使用 Record 替代传统 DTO,代码量大幅减少
// 传统 DTO 需要 50+ 行,Record 只需 1 行
public record UserDTO(
    Long id,
    String name,
    String email,
    int age
) {}

// Record 与 Spring MVC 配合
@RestController
@RequestMapping("/api/users")
public class UserController {
    @GetMapping("/{id}")
    public UserDTO getUser(@PathVariable Long id) {
        return userService.findById(id);
    }

    @PostMapping
    public UserDTO createUser(@RequestBody UserDTO dto) {
        return userService.create(dto);
    }
}

密封建模领域

// 用密封类建模支付方式,编译器保证穷举
public sealed interface PaymentMethod {
    record CreditCard(String number, String expiry) implements PaymentMethod {}
    record WeChatPay(String openId) implements PaymentMethod {}
    record BankTransfer(String account, String bank) implements PaymentMethod {}
}

// 处理支付时,编译器会检查是否覆盖所有情况
public String processPayment(PaymentMethod method) {
    return switch (method) {
        case PaymentMethod.CreditCard(var num, var exp)
            -> "信用卡支付: " + num.substring(num.length() - 4);
        case PaymentMethod.WeChatPay(var openId)
            -> "微信支付: " + openId;
        case PaymentMethod.BankTransfer(var acct, var bank)
            -> "银行转账: " + bank + " " + acct;
    };
}

注意事项

  • Record 是不可变的,所有字段都是 final,不能继承其他
  • 密封的 permits 列表必须与密封在同一包中(模块化项目中可在同一模块
  • 模式匹配的 switch 必须穷举所有可能,否则编译错误
  • 文本块中的尾部空格会被自动去除,注意缩进处理
  • List.of() 等工厂方法创建的是不可变集合,不能添加或删除元素
  • 虚拟线程在 Java 21 正式发布,Java 19-20 为预览特性

进阶用法

作用域值(Scoped Values,预览)

// 作用域值替代 ThreadLocal,在虚拟线程中更安全
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

// 在方法中绑定作用域值
ScopedValue.where(CURRENT_USER, user).run(() -> {
    processRequest(); // 在此范围内可以访问 CURRENT_USER
});

// 在任意深层调用中获取
void processRequest() {
    User user = CURRENT_USER.get(); // 获取当前用户
    doSomething(user);
}

Foreign Function & Memory API(预览)

// Java 22+ 的外部函数和内存 API,替代 JNI
try (Arena arena = Arena.ofConfined()) {
    // 分配外部内存
    MemorySegment segment = arena.allocate(100);

    // 写入数据
    segment.setString(0, "Hello, FFM API!");

    // 读取数据
    String value = segment.getString(0);
    System.out.println(value);
}