JDK17 新特性

LTS:

LTS 是**Long-Term Support(长期支持)**的缩写,是 Oracle 及 OpenJDK 社区为企业级应用提供的「稳定版本保障」。

非LTS版本每6个月发布一次 ,更像是"尝鲜版"。它们包含最新的功能,但官方支持周期很短(通常只有6个月)。适合个人开发者体验新特性。
LTS版本大约每2-3年发布一次,会获得长达数年的官方支持,包括安全更新和错误修复。JDK 17作为LTS版本,将获得至少8年的支持(直到2029年9月),这为企业提供了升级和长期运行的稳定基础。

从 JDK 8 开始,Java 确立了「每 6 个月发布一个非 LTS 版本,每 3 年发布一个 LTS 版本」的迭代节奏:

JDK 8 LTS(2014 年发布 ):经典中的经典,支撑了无数企业系统,支持周期已接近尾声,目前仅提供关键安全补丁;
JDK 11 LTS(2018 年发布 ):继 JDK 8 后的又一主流版本,引入模块化雏形,目前仍有大量企业在使用;
JDK 17 LTS(2021 年发布) :沉淀了 JDK 9 到 JDK 16 的所有优秀特性,性能、安全性、语法简洁度均远超 JDK 8/11;
JDK 21 LTS(2023 年发布 ):正在普及的版本,在 JDK 17 基础上新增虚拟线程等特性,未来将逐步替代 JDK 17。

密封类(Sealed Classes)

什么是密封类?

密封类是一种「限制继承关系」的类,它可以明确指定「哪些子类可以继承自己」,相当于给父类加了一个「继承白名单」,避免无关子类随意继承导致的逻辑混乱。

在 JDK 8 中,我们定义一个父类后,无法限制其他开发者随意创建子类,尤其是在领域模型、状态机设计中,很容易出现「继承泛滥」的问题。

密封类的核心语法是**sealed(声明密封类)和 permits(指定允许继承的子类)** ,子类必须显式声明为**final/sealed/non-sealed** 三者之一。

java 复制代码
// 1. 密封类定义
public sealed class Shape permits Circle, Rectangle, Triangle {
    public double area() {
        return 0;
    }
}

// 2. 允许的子类(必须是 final、sealed 或 non-sealed)
public final class Circle extends Shape {
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

public final class Rectangle extends Shape {
    @Override
    public double area() {
        return width * height;
    }
}

// 3. 密封接口
public sealed interface Vehicle permits Car, Truck {
    void drive();
}

public final class Car implements Vehicle {
    public void drive() {
        System.out.println("Driving car");
    }
}

模式匹配(Pattern Matching)和增强的 switch 表达

1. switch 支持模式匹配(JDK 17 正式特性)

可以直接在 switch 中进行类型判断和变量声明,无需手动强制转换。

java 复制代码
// JDK 17 之前的写法
public String format(Object obj) {
    if (obj instanceof Integer) {
        Integer i = (Integer) obj;
        return "整数:" + i;
    } else if (obj instanceof String) {
        String s = (String) obj;
        return "字符串:" + s;
    } else {
        return "未知类型";
    }
}

// JDK 17 的写法 - 使用 switch 模式匹配
public String format(Object obj) {
    return switch (obj) {
        case Integer i -> "整数:" + i;
        case String s -> "字符串:" + s;
        case Double d -> "小数:" + d;
        default -> "未知类型";
    };
}

2. 箭头语法(->)简化代码

更简洁的 lambda 风格语法,自动返回值。

java 复制代码
// 传统 switch 写法
public int getDays(int month, int year) {
    int days;
    switch (month) {
        case 1: case 3: case 5: case 7: case 8: case 10: case 12:
            days = 31;
            break;
        case 4: case 6: case 9: case 11:
            days = 30;
            break;
        case 2:
            days = isLeapYear(year) ? 29 : 28;
            break;
        default:
            throw new IllegalArgumentException("无效的月份");
    }
    return days;
}

// JDK 17 增强写法
public int getDays(int month, int year) {
    return switch (month) {
        case 1, 3, 5, 7, 8, 10, 12 -> 31;
        case 4, 6, 9, 11 -> 30;
        case 2 -> isLeapYear(year) ? 29 : 28;
        default -> throw new IllegalArgumentException("无效的月份");
    };
}

3. 合并多个 case 标签

使用逗号分隔多个值,更简洁。

java 复制代码
public String getSeason(String month) {
    return switch (month) {
        case "三月", "四月", "五月" -> "春季";
        case "六月", "七月", "八月" -> "夏季";
        case "九月", "十月", "十一月" -> "秋季";
        case "十二月", "一月", "二月" -> "冬季";
        default -> "未知";
    };
}

4. 不需要 break 语

箭头语法自动处理 break,避免遗漏导致的穿透问题。

java 复制代码
// 传统写法容易忘记 break
switch (day) {
    case MONDAY:
        System.out.println("周一");
        break;  // 容易忘记
    case TUESDAY:
        System.out.println("周二");
        break;
}

// JDK 17 写法 - 无需 break
switch (day) {
    case MONDAY -> System.out.println("周一");
    case TUESDAY -> System.out.println("周二");
    case WEDNESDAY -> System.out.println("周三");
}

5. 与密封类(Sealed Classes)完美配合

当 switch 作用于密封类时,如果覆盖所有子类,可以省略 default。

java 复制代码
// 假设 Shape 是密封类
public sealed class Shape permits Circle, Rectangle, Triangle {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
public final class Triangle extends Shape {}

// 计算面积 - 无需 default,编译器会检查是否完整
public double calculateArea(Shape shape) {
    return switch (shape) {
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Triangle t -> 0.5 * t.base() * t.height();
        // 不需要 default,编译器确保已覆盖所有情况
    };
}

简化的集合操作

虽然 JDK 17 本身没有新增集合简化特性,但它集成了之前版本的所有优秀特性:
JDK 8: Stream API、Lambda、默认方法
JDK 9: of() 工厂方法、takeWhile/dropWhile
JDK 10: var 局部变量类型推断
JDK 16: toList()/toMap()/toSet() 简化

java 复制代码
// 场景 1:数据过滤和转换
public List<String> processUsers(List<User> users) {
    return users.stream()
        .filter(u -> u.getAge() >= 18)
        .map(User::getName)
        .map(String::toUpperCase)
        .toList();
}

// 场景 2:分组统计
public Map<String, Long> countByDept(List<Employee> employees) {
    return employees.stream()
        .collect(Collectors.groupingBy(
            Employee::getDepartment,
            Collectors.counting()
        ));
}

// 场景 3:集合去重和排序
public List<Product> getUniqueProducts(List<Product> products) {
    return products.stream()
        .distinct()
        .sorted(Comparator.comparing(Product::getPrice).reversed())
        .toList();
}

// 场景 4:Map 聚合计算
public void updateInventory(Map<String, Integer> inventory, 
                           List<Item> items) {
    for (Item item : items) {
        inventory.merge(item.getCode(), item.getQuantity(), Integer::sum);
    }
}

文本块(Text Blocks)

什么是文本块?

文本块使用三个双引号(""") 来包裹多行字符串,无需手动添加换行符和转义字符。
基本语法:

java 复制代码
// 传统写法 - 需要拼接和转义
String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello World</p>\n" +
              "    </body>\n" +
              "</html>\n";

// JDK 17 文本块写法 - 简洁清晰
String html = """
              <html>
                  <body>
                      <p>Hello World</p>
                  </body>
              </html>
              """;

主要特性:

  1. 自动处理换行,无需手动添加 \n,保持代码的自然格式。

2.自动缩进处理,编译器会自动去除每行开头的公共空白部分

  1. 无需转义特殊字符,可以直接包含双引号、反斜杠等。

  2. 支持字符串模板,可以嵌入变量和表达式。

模块化系统(JPMS)

JDK 17 中的模块化系统(JPMS - Java Platform Module System),也称为 Project Jigsaw,是 Java 9 引入的一项革命性特性,在 JDK 17 中已经非常成熟稳定。

什么是 JPMS?

JPMS 是一种将 Java 平台和相关应用程序划分为一组模块的机制 ,目的是:
更强的封装性 :只暴露需要的类和方法
清晰的依赖管理 :显式声明模块间的依赖关系
更好的可维护性 :减少类路径冲突(JAR Hell)
更小的运行时:可以只包含需要的模块

java 复制代码
// 示例:定义一个库存管理模块
module com.example.inventory {
    // 声明依赖的其他模块
    requires java.sql;
    requires java.logging;
    
    // 导出的包(外部可访问)
    exports com.example.inventory.service;
    exports com.example.inventory.model;
    
    // 开放的包(允许反射访问)
    opens com.example.inventory.impl;
    
    // 使用的服务实现
    uses com.example.inventory.provider.InventoryProvider;
    
    // 提供的服务实现
    provides com.example.inventory.provider.InventoryProvider
        with com.example.inventory.provider.DatabaseInventoryProvider;
}

场景背景(JDK 8 的痛点)

在 JDK 8 中,我们所有的业务代码、引入的第三方 Jar 包(比如 Spring、MyBatis),都被一股脑地放在「类路径(classpath)」这个「大箱子」里,开发和运维时会遇到 4 个让人头疼的问题:

类冲突(最常见) :不同 Jar 包里有同名的类(比如两个不同版本的工具包都有 com.example.utils.StringUtils),JVM 加载时不知道该选哪个,直接抛出 ClassNotFoundException 或 NoClassDefFoundError,排查起来要翻遍所有 Jar 包,耗时耗力;
隐式依赖(维护噩梦) :项目里用了某个 Jar 包的类,但没有明确声明「我依赖这个 Jar 包」,后续其他人接手项目、或部署到新环境时,很容易漏掉这个依赖,导致项目启动失败;
启动缓慢(微服务痛点) :JVM 启动时,会把类路径下所有的类都加载一遍,哪怕有些类从头到尾都没被使用过(比如某个 Jar 包的冷门功能),不仅启动速度慢,还占用大量内存,在微服务场景下,这个问题会被放大(很多微服务需要快速启动、快速扩容);
无强封装(耦合过高) :只要类是 public 修饰的,其他任何代码都能访问它,哪怕这个类是某个框架的内部实现类、不希望被外部调用,这就导致项目之间耦合过高,后续修改内部代码时,很容易影响到外部调用者。

垃圾收集器

JDK 8 默认 GC:Parallel GC(并行收集器)

G1 在 JDK 8 中是可选、非默认,必须手动加 -XX:+UseG1GC 才能启用。
JDK 9 → JDK 17 官方默认 GC:G1 GC

从 JDK 9 开始,OpenJDK 将默认 GC 从 Parallel GC 改为 G1 GC,并一直延续到 JDK 17、JDK 18、JDK 19、JDK 20。
JDK 17 官方默认仍然是 G1 GC,不是 ZGC。

ZGC 在 JDK 17 中的定位

ZGC 在 JDK 15 已结束实验,成为正式生产特性;
JDK 17 中 ZGC 是高可用、可生产的正式 GC,但不是默认 GC;

必须通过 -XX:+UseZGC 手动显式启用。
JDK 21 开始ZGC 成为默认 GC 的版本

从 JDK 21 开始,OpenJDK 才将默认 GC 从 G1 改为 ZGC。

什么是 ZGC?

ZGC(Z Garbage Collector)是 Oracle 主导开发的一款可扩展、低延迟、高吞吐的垃圾收集器。

JDK 8 Parallel GC(并行垃圾回收器)

java 复制代码
┌─────────────────────────────────────┐
│   Young Generation (Eden + S0 + S1) │
│   ───────────────────────────────   │
│   Minor GC: 多线程并行复制          │
└─────────────────────────────────────┘
              ↓
┌─────────────────────────────────────┐
│      Old Generation                 │
│   Full GC: 标记 - 整理(STW)        │
└─────────────────────────────────────┘

JDK 17 G1 GC(Garbage-First)

java 复制代码
┌─────────────────────────────────────────────┐
│  Heap = Regions (固定大小,通常 1-32MB)     │
│  ┌───┬───┬───┬───┬───┬───┬───┬───┐         │
│  │ E │ E │ S │ S │ O │ O │ H │ H │         │
│  ├───┼───┼───┼───┼───┼───┼───┼───┤         │
│  │ E │ E │ S │ S │ O │ O │ M │ M │         │
│  └───┴───┴───┴───┴───┴───┴───┴───┘         │
│  E=Eden, S=Survivor, O=Old, H=Humongous    │
│  M=Metaspace                               │
└─────────────────────────────────────────────┘

回收策略:优先回收价值最大的 Region(垃圾最多)

G1的四个阶段

java 复制代码
1. Young Collection(年轻代回收)
   - STW,并行收集
   - Eden → Survivor/To Space

2. Concurrent Marking(并发标记)
   - 与应用程序并发执行
   - 标记存活对象

3. Remark(最终标记)
   - STW,处理并发期间的变化
   - 使用 SATB 算法

4. Cleanup(清理)
   - STW + 并发
   - 回收空 Region,统计存活数据

JDK 17 ZGC(Z Garbage Collector)

java 复制代码
┌─────────────────────────────────────────────┐
│  ZGC 并发周期                                │
│                                              │
│  1. Marking (并发)                           │
│     ├─ Mark Stack Scan                      │
│     └─ Object Scan                          │
│                                              │
│  2. Reference Processing (并发)              │
│                                              │
│  3. Remap (并发 + 短暂 STW)                  │
│     ├─ Relocate Live Objects                │
│     └─ Update References (Load Barriers)    │
│                                              │
│  停顿时间:< 1ms(几乎无感知)               │
└─────────────────────────────────────────────┘

核心技术:

java 复制代码
1. Colored Pointers(染色指针)
   - 64 位指针中用几位标记对象状态
   - 无需额外内存记录引用信息

2. Load Barriers(读屏障)
   - 对象访问时检查并更新引用
   - 并发重映射

3. Region 分层
   - Small (<256KB)
   - Large (≥256KB)
相关推荐
独自破碎E2 小时前
【面试真题拆解】Spring事务机制
java·spring·面试
我是咸鱼不闲呀2 小时前
力扣Hot100系列21(Java)——[多维动态规划]总结(不同路径,最小路径和,最长回文子串,最长公共子序列, 编辑距离)
java·leetcode·动态规划
lihao lihao2 小时前
二分查找
java·数据结构·算法
Albert Edison2 小时前
【C++11】可变参数模板
java·开发语言·c++
代码栈上的思考2 小时前
消息队列持久化:文件存储设计与实现全解析
java·前端·算法
sg_knight2 小时前
设计模式实战:策略模式(Strategy)
java·开发语言·python·设计模式·重构·架构·策略模式
麦麦鸡腿堡2 小时前
JavaWeb_SpringBootWeb,HTTP协议,Tomcat快速入门
java·开发语言
一然明月2 小时前
Qt QML 锚定(Anchors)全解析
java·数据库·qt