JDK 17 新特性详解 —— Java 的第三个 LTS 版本

📅

✍️


JDK 17 新特性详解 —— Java 的第三个 LTS 版本

JDK 17 于 2021 年 9 月 14 日正式发布,是继 JDK 8 和 JDK 11 之后的第三个长期支持(LTS)版本。本文全面梳理 JDK 17 带来的重要新特性与改进。

目录

  1. 为什么 JDK 17 如此重要
  2. 语言特性增强
  3. 库与 API 更新
  4. 性能与运行时改进
  5. 安全性增强
  6. 移除与弃用
  7. 从 JDK 8/11 迁移指南
  8. 总结

为什么 JDK 17 如此重要

JDK 17 是 Java 生态的一个里程碑版本,原因如下:

维度 说明
LTS 定位 长期支持版本,Oracle 提供至少到 2029 年的支持
Spring 生态 Spring Boot 3.x / Spring Framework 6.x 强制要求 JDK 17+
性能飞跃 相比 JDK 11 有 10%~30% 的性能提升
现代 Java 集成了 JDK 12~17 积累的所有语言和 JVM 改进

关键事实: 如果你还在用 JDK 8,JDK 17 是迄今为止最值得升级的目标版本。它稳定、快速,并被主流框架广泛支持。

语言特性增强

1. 密封类(Sealed Classes)— JEP 409(正式特性)

密封类允许你控制哪些类可以继承或实现某个类或接口:

// 定义一个密封类,只允许 Dog 和 Cat 继承
public sealed class Animal permits Dog, Cat, Bird {
    public String name() { return "动物"; }
}

// permits 列表中的子类必须是 final、sealed 或 non-sealed
public final class Dog extends Animal {
    @Override
    public String name() { return "狗"; }
}

public final class Cat extends Animal {
    @Override
    public String name() { return "猫"; }
}

// non-sealed 表示开放继承,不再受限
public non-sealed class Bird extends Animal {
    @Override
    public String name() { return "鸟"; }
}

// 现在其他类可以自由继承 Bird
public class Sparrow extends Bird { }

优势:

  • 编译期类型安全检查 —— 穷尽所有子类,配合 switch 表达式实现完美模式匹配
  • 适合建模有限状态的领域(订单状态、支付方式等)

2. 模式匹配 for Switch(预览)— JEP 406

首次将模式匹配引入 switch,让类型判断和值提取一气呵成:

// 传统写法 —— 又臭又长
static String formatter(Object o) {
    if (o instanceof Integer i) {
        return "整数: " + i;
    } else if (o instanceof Long l) {
        return "长整数: " + l;
    } else if (o instanceof Double d) {
        return "浮点数: " + d;
    } else if (o instanceof String s) {
        return "字符串: " + s;
    } else {
        return "未知类型";
    }
}

// JDK 17 模式匹配 switch —— 清爽优雅
static String formatter(Object o) {
    return switch (o) {
        case Integer i -> "整数: " + i;
        case Long l    -> "长整数: " + l;
        case Double d  -> "浮点数: " + d;
        case String s  -> "字符串: " + s;
        default        -> "未知类型";
    };
}

注意:此特性在 JDK 17 中为第一轮预览,正式版需等到 JDK 21。但值得提前了解。

3. 文本块增强(Text Blocks)— JEP 378

JDK 13~14 作为预览引入,JDK 15 正式发布。JDK 17 中已成为稳定功能:

// JDK 8 时代 —— 痛苦
String json = "{\n" +
              "  \"name\": \"张三\",\n" +
              "  \"age\": 28,\n" +
              "  \"city\": \"北京\"\n" +
              "}";

// JDK 17 文本块 —— 自在
String json = """
    {
      "name": "张三",
      "age": 28,
      "city": "北京"
    }
    """;

// 直接写 SQL 不再是噩梦
String sql = """
    SELECT u.id, u.name, o.total
    FROM users u
    JOIN orders o ON u.id = o.user_id
    WHERE u.status = 'ACTIVE'
    ORDER BY o.total DESC
    """;

4. Record 类(Records)— JEP 395(正式特性)

JDK 14~15 预览,JDK 16 正式发布。JDK 17 开箱即用:

// 一行代码替代传统 JavaBean 的几十行样板代码
public record User(Long id, String name, String email) {}

// 编译器自动生成:
//   - 私有的 final 字段
//   - 全参构造器
//   - 所有字段的访问器方法(id()、name()、email())
//   - equals()、hashCode()、toString()
//   - 不可变 —— 字段全部 final

// 使用
var user = new User(1L, "张三", "zhangsan@example.com");
System.out.println(user.name());       // 张三
System.out.println(user);              // User[id=1, name=张三, email=zhangsan@example.com]

// 可自定义紧凑构造器
public record User(Long id, String name, String email) {
    public User {
        if (id == null || id <= 0) throw new IllegalArgumentException("无效的 ID");
        if (name == null || name.isBlank()) throw new IllegalArgumentException("名称不能为空");
    }
}

最佳实践: Record 非常适合用作 DTO、VO、配置项、返回结果等不可变数据载体。

5. 增强的 instanceof(JEP 394)— 正式特性

模式匹配 instanceof 在 JDK 16 正式发布,JDK 17 已是标准:

// 旧写法 —— 类型检查 + 强制转换,两步分离
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toLowerCase());
}

// 新写法 —— 一步到位
if (obj instanceof String s) {
    System.out.println(s.toLowerCase());
}

// 支持复杂条件(注意 && 短路语义)
if (obj instanceof String s && s.length() > 5) {
    System.out.println("长字符串: " + s);
}

6. Switch 表达式(Switch Expressions)— JEP 361

JDK 12~13 预览,JDK 14 正式发布:

// 传统 switch —— 容易因遗漏 break 导致 fall-through 错误
String dayType;
switch (day) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        dayType = "工作日";
        break;
    case SATURDAY:
    case SUNDAY:
        dayType = "周末";
        break;
    default:
        throw new IllegalArgumentException("无效: " + day);
}

// 新 switch 表达式 —— 安全、精简、可直接赋值
String dayType = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "工作日";
    case SATURDAY, SUNDAY -> "周末";
};

库与 API 更新

1. 增强的伪随机数生成器(JEP 356)

全新的 RandomGenerator 接口,提供多种算法实现:

// 使用不同的随机算法
RandomGenerator rng = RandomGenerator.of("L64X128MixRandom");
System.out.println(rng.nextInt(100));

// 遍历所有可用的算法
RandomGeneratorFactory.all()
    .map(RandomGeneratorFactory::name)
    .forEach(System.out::println);
// 输出示例: L32X64MixRandom, L64X128MixRandom, L64X128StarStarRandom,
//          L128X256MixRandom, L128X1024MixRandom, Xoroshiro128PlusPlus, Xoshiro256PlusPlus...

2. 全新的 java.time 格式引擎(核心改进)

// JDK 17 开始,java.time 使用自定义格式引擎,性能更高
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now();
System.out.println(formatter.format(now));  // 2026-06-09 11:30:45

3. Stream API 增强

// Stream.toList() —— 直接返回不可变 List,比 collect(Collectors.toList()) 更简洁
List<String> names = users.stream()
    .map(User::getName)
    .toList();  // JDK 16+ 引入,JDK 17 标配

// mapMulti —— 扁平映射的新方式,避免 Stream<Stream<T>> 中间开销
Stream.of(1, 2, 3)
    .mapMulti((num, consumer) -> {
        consumer.accept(num);
        consumer.accept(num * 10);
    })
    .forEach(System.out::println);
// 输出: 1, 10, 2, 20, 3, 30

4. List.of() / Set.of() / Map.of() 不可变集合

JDK 9 引入,JDK 17 中已是创建小型只读集合的首选:

List<String> colors = List.of("红", "绿", "蓝");
Set<Integer> nums = Set.of(1, 2, 3);
Map<String, Integer> scores = Map.of("张三", 90, "李四", 85, "王五", 92);

性能与运行时改进

1. 新的 macOS 渲染管线(JEP 382)— Apple Metal 加速

JDK 17 在 macOS 上使用 Apple Metal API 替代过时的 OpenGL,告别了需要 -Djdk.gtk.version=2 的蹩脚配置,UI 渲染更流畅。

2. 向量 API(孵化)— JEP 414

面向 CPU 向量指令集的孵化中 API,适用于机器学习、加密、图像处理等密集计算场景:

// 孵化中模块,需要 --add-modules jdk.incubator.vector 编译
// IntVector va = IntVector.fromArray(SPECIES_256, arrayA, 0);
// IntVector vb = IntVector.fromArray(SPECIES_256, arrayB, 0);
// IntVector vc = va.add(vb);

3. ZGC 增强

  • 并发线程栈处理
  • 更低延迟的垃圾回收
  • macOS 与 Windows 平台的全面支持

4. G1 和 Parallel GC 持续优化

  • 更好的 NUMA 感知
  • 大页内存支持改进
  • 更快的 Full GC

5. 外部函数与内存 API(孵化)— JEP 412

取代 JNI 的下一代方案,更安全、更高效地调用本地代码和操作堆外内存:

// 孵化中,将来的标准特性
// MemorySegment segment = MemorySegment.allocateNative(100);
// segment.set(ValueLayout.JAVA_INT, 0, 42);

安全性增强

特性 说明
JEP 411 弃用 Security Manager,为最终移除做准备
JEP 415 上下文特定的反序列化过滤器,防止反序列化攻击
TLS 1.3 全面支持,默认启用
EdDSA 签名 支持 Edwards-Curve 数字签名算法(Ed25519、Ed448)
证书吊销检查改进 CRL 和 OCSP 检查性能与可靠性提升

移除与弃用

  • Applet API 正式移除 —— 时代的眼泪
  • RMI Activation 被弃用
  • AOT 编译器 (jaotc) 与实验性 Graal JIT 被移除
  • Security Manager 被标记为弃用 (JEP 411)
  • 默认禁用密封(封装) 的内部 API(需要 --add-opens

从 JDK 8/11 迁移指南

推荐迁移路径

JDK 8  ──────►  JDK 11  ──────►  JDK 17  ──────►  JDK 21(下一 LTS)
                    ↑
              直接跳迁到 17 也可行(多数项目适用)

常见迁移问题与解决

问题 说明 解决方案
内部 API 访问受限 旧项目常依赖 sun.misc.Unsafe 添加 --add-opens 启动参数
-XX:+UseParallelGC 等 GC 选项变化 7 之前默认 CMS,8 之后默认 G1 检查 GC 日志,按需调整
Lombok 兼容性 旧版 Lombok 不支持 JDK 17 升级到 Lombok 1.18.22+
Maven/Gradle 版本 老版本编译器不支持 Java 17 Maven ≥ 3.6.3, Gradle ≥ 7.3
--illegal-access=permit 不再有效 JDK 17 默认强封装 使用 --add-opens 精确开放

推荐的启动参数(过渡阶段)

# 最小过渡配置
java --add-opens java.base/java.lang=ALL-UNNAMED \
     --add-opens java.base/java.util=ALL-UNNAMED \
     -jar your-app.jar

Maven 编译器配置

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

Gradle 配置

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

总结

JDK 17 不是一个”多了几个语法糖而已”的版本,它代表了 Java 现代化的一个重要节点:

  • 语言层面: Records、密封类、模式匹配、文本块,让代码更简洁安全
  • 性能层面: ZGC、G1、Metal 渲染、向量 API,全面提升运行时表现
  • 生态层面: Spring Boot 3 强制 JDK 17 底线,驱动整个行业升级
  • 安全层面: 序列化过滤、EdDSA、TLS 1.3 默认化,安全性再上台阶

如果你还在 JDK 8 上”岁月静好”,是时候迈出这一步了。JDK 17 不仅是一个 LTS,更是现代 Java 开发的新基线

进一步阅读:

OpenJDK 17 官方发布说明

JEP 索引

Spring Boot 3 迁移指南

*最后更新:2026 年 6 月*