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>
""";
主要特性:
- 自动处理换行,无需手动添加 \n,保持代码的自然格式。
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)