2026年3月17日,Oracle 发布了 JDK 26。从 JDK 8 到 JDK 26,Java 已经走过了近 18 年。
很多人对 Java 的印象还停留在"JDK 8 够用了"的阶段,殊不知这门"老语言"早已在悄悄自我革新。
var类型推断、文本块、Records、Sealed Classes、模式匹配、虚拟线程……每半年一次的版本更新,都让 Java 代码变得更简洁、更现代、更有表现力。本文从 JDK 9 一路写到 JDK 26,每个版本挑最亮眼的 1-2 个特性,用「以前这么写 vs 现在这么写」的对比方式,让你直观感受这些变化的魅力。先郑重声明:JDK 版本众多,每个版本的 JEP 少则几个、多则十几个,本文只挑日常写代码最常用、对代码质量影响最大的特性来讲,完整的 JEP 列表会附在文末。
JDK 9:模块化系统来了,文本块初露锋芒
是什么
JDK 9 是继 JDK 8 之后最大的架构变革,引入了 JPMS(Java Platform Module System) ,把 Java 应用从"一堆 JAR 包"变成了"有边界、可复用的模块"。同时,文本块(Text Blocks)以预览特性登场。
模块系统:以前这么写
// 没模块之前,JAR 包之间依赖关系不清晰
// 全局可见,想用什么就拿什么,一不小心就耦合成屎山
现在这么写
// module-info.java
module com.example.myapp {
requires com.example.utils; // 明确依赖
exports com.example.api; // 明确导出哪些包
opens com.example.internal; // 明确哪些包可反射
}
模块化让大型项目有了更清晰的边界。Spring Boot 3 和 Jakarta EE 都在往模块化迁移,这是现代 Java 架构的必经之路。
文本块预览:以前这么写
String json = "{\n" +
" "name": "张三",\n" +
" "age": 18\n" +
"}";
现在这么写
String json = """
{
"name": "张三",
"age": 18
}
""";
文本块用三引号 """ 包裹多行字符串,从此告别 + "\n" + 的拼接噩梦。特别适合写 SQL、JSON、HTML 等多行字符串场景。
JDK 10:var 横空出世,样板代码砍一半
是什么
JDK 10 带来了 var 局部类型推断关键字,编译器会自动推断变量类型,开发者不需要重复写类型名。
以前这么写
String name = user.getName();
List<Map<String, Object>> data = queryData();
Optional<UserEntity> userOpt = userRepository.findById(id);
现在这么写
var name = user.getName();
var data = queryData();
var userOpt = userRepository.findById(id);
简洁度直接拉满,而且类型安全——var 不是弱类型,编译后还是强类型的。
注意
var只能用局部变量,不能用字段、方法参数、返回值var必须有初始值,否则编译器推不出来- 链式调用特别长的时候慎用,不然回头看代码都不知道这个变量是什么类型
JDK 11:HTTP Client 终于原生支持,ZGC 上线
是什么
JDK 11 是一个 LTS 版本,带来了两个重磅特性:原生 HTTP Client API(支持 HTTP/2 和异步)和 ZGC(低延迟垃圾收集器)。
以前这么写(HTTP 请求)
// 用 HttpURLConnection,API 极其繁琐
URL url = new URL("https://api.example.com/data");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
// 还要自己处理流、解析响应……写了20行才拿到一个JSON
现在这么写
// JDK 11 原生 HTTP Client
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.GET()
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.body());
支持同步和异步,支持 HTTP/2,性能比 HttpURLConnection 好出一大截。JDK 26 还给它加上了 HTTP/3 支持(见后文)。
ZGC:以前不敢想的低延迟
# 启动参数加一行,打开 ZGC
java -XX:+UseZGC -Xmx16g myapp.jar
ZGC 的停顿时间控制在毫秒级以内(通常 < 1ms),无论堆多大都不会有明显停顿。以前 GC 导致的业务卡顿,用 ZGC 几乎可以忽略不计。
JDK 12-13:Switch 表达式和文本块持续打磨
是什么
JDK 12-13 对两个预览特性进行了持续迭代:Switch 表达式和文本块。
Switch 表达式预览:以前这么写(JDK 8)
int result;
switch (day) {
case MONDAY:
case FRIDAY:
result = 6;
break;
case TUESDAY:
result = 7;
break;
// ...漏写一个 break 就是bug
default:
result = 0;
}
现在这么写(JDK 14+ 箭头语法)
int result = switch (day) {
case MONDAY, FRIDAY -> 6;
case TUESDAY -> 7;
case THURSDAY -> 8;
default -> 0;
};
箭头语法返回值,不需要 break,也不会漏掉 case,简洁又安全。
文本块从预览到接近正式
JDK 13 文本块增加了两个转义序列:\s(保留末尾空格)和 ""(在文本块内表示双引号),让它更接近生产可用。
JDK 14-15:Records 和 Sealed Classes 登场,POJO 大解放
是什么
JDK 14-15 是两个版本的预览期,Records(不可变数据类)和 Sealed Classes(密封类)先后进入预览,日渐成熟。
Records:以前这么写
写一个只存数据的类,要手写构造器、Getter、equals、hashCode、toString:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
@Override
public boolean equals(Object o) { /* 15行 */ }
@Override
public int hashCode() { /* 5行 */ }
@Override
public String toString() { return "Point{x=" + x + ", y=" + y + '}'; }
}
现在这么写
public record Point(int x, int y) {}
就一行,自动获得:私有 final 字段、全参构造器、Getter(注意是 x() 不是 getX())、equals、hashCode、toString。
Sealed Classes 预览:精确控制谁可以继承
// Shape 只有 Circle 和 Square 能继承
public sealed class Shape permits Circle, Square {}
public final class Circle extends Shape {}
public non-sealed class Square extends Shape {}
适合 DDD 值对象、策略模式、规则引擎等场景——你想让谁继承,你说了算。
JDK 16:Records 和 instanceof 模式匹配正式转正
是什么
Records 和 instanceof 模式匹配(JDK 14 预览)正式成为标准特性,可以放心在生产环境使用。
instanceof 模式匹配:以前这么写
if (obj instanceof String) {
String s = (String) obj; // 重复写类型,还要手动强转
if (s.length() > 5) {
System.out.println(s.toUpperCase());
}
}
现在这么写(JDK 16+)
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase()); // s 直接用,不需要强转
}
instanceof 后面直接声明变量 s,类型自动推断,不需要再写 (String) obj 强转。
JDK 17:Sealed Classes 正式转正,Switch 表达式也稳了
是什么
JDK 17 是继 JDK 11 之后的又一个 LTS 版本。Sealed Classes 和 Switch 表达式在这一版本正式 GA,生产使用无顾虑。
同时 JDK 17 开始,免费提供 JDK 版本从"仅供开发"升级为生产使用也免费(Oracle JDK),这对企业用户是个重大利好。
JDK 18-20:默默积累,String Templates 初现
是什么
这几个版本的核心变化主要在库层面,亮点不多但积累很深:
- JDK 18:默认启用
UTF-8编码(再也不用担心中文乱码了),引入了java.net.spi.InetAddress的改进 - JDK 19:虚拟线程(Project Loom)第一次预览登场!
- JDK 20:虚拟线程第二次预览,模式匹配持续完善
虚拟线程在 JDK 19 第一次预览出来后,社区反响强烈。这个特性从根本上解决了 Java 并发编程中"线程是稀缺资源"的痛点。
JDK 21:史上最强 LTS,虚拟线程正式转正 ⭐⭐⭐
是什么
JDK 21 是 Java 历史上里程碑式的 LTS 版本,虚拟线程正式转正、Switch 模式匹配完整落地、Sequenced Collections 补全集合框架最后一块短板。
虚拟线程:以前这么写
// 1000个并发任务 = 1000个线程 = 内存爆炸
ExecutorService executor = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
final int taskId = i;
executor.submit(() -> {
doIOOperation(taskId); // 大多数时间在等 IO
return null;
});
}
现在这么写(JDK 21+)
// 100万个虚拟线程同时运行,轻轻松松
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i ->
executor.submit(() -> doIOOperation(i))
);
}
每个任务一个协程,可以 hold 住百万级并发 IO!内存占用从 GB 级降到 MB 级。
注意:虚拟线程只适合 IO 密集型任务。CPU 密集型任务还是老老实实用线程池。
Sequenced Collections:终于有"有序集合"标准接口了
// JDK 21 之前,没有统一的方式获取第一个/最后一个元素
// JDK 21 引入 SequencedCollection 接口
SequencedCollection<String> list = new ArrayList<>();
list.addFirst("first");
list.addLast("last");
System.out.println(list.getFirst()); // first
System.out.println(list.getLast()); // last
Switch 模式匹配完整版(JEP 441)
// 以前 switch 只能匹配常量,现在可以匹配类型和结构
String formatted = switch (obj) {
case Integer i -> String.format("int: %d", i);
case String s -> String.format("str: %s", s.toUpperCase());
case null, default -> "unknown";
};
JDK 22-23:外部函数 API 转正,未命变量登场
是什么
JDK 22-23 带来了大量 Preview 特性成熟落地,以及一些语言级别的细微改进。
未命变量和模式(JEP 456 正式特性)
// 不需要用到的变量,用 _ 占位
for (int i = 0, _ = initialize(i); i < 10; i++) {
System.out.println(i);
}
// switch 中也不需要每个 case 都用变量
switch (shape) {
case Circle _ -> drawCircle();
case Rectangle _ -> drawRectangle();
}
外部函数和内存 API(JEP 454 正式特性)
这是 Java 历史上里程碑式的特性——Java 程序可以直接调用 C 函数和操作原生内存,不再需要 JNI:
// JDK 22 外部函数 API(无需 JNI)
MemorySegment segment = MemorySegment.allocateNative(100);
这为高性能计算、AI 推理等领域打开了大门,未来 Java 可以直接对接 native 生态。
文本块终于正式(JDK 22)
字符串模板在 JDK 21 预览后,JDK 22 继续打磨(最终在 JDK 25 正式转正)。
Markdown 文档注释(JEP 467 正式特性)
/**
* # 计算两个数的和
*
* 这是一个 **加法** 方法。
*
* @param a 第一个数
* @param b 第二个数
*/
public static int add(int a, int b) { return a + b; }
JavaDoc 里终于可以写 Markdown 了,文档可读性大幅提升。
JDK 24:紧凑对象头上线,内存省一大截
是什么
JDK 24 带来了紧凑对象头(JEP 519 正式化),将对象头从 96-128 位缩减至 64 位,堆内存占用显著下降。
# 启动时加上这个参数
java -XX:+UseCompactObjectHeaders -Xmx16g myapp.jar
对于高并发场景下大量小对象的 Java 应用(比如 RPC 框架、缓存系统),这个优化能让内存使用量直接降 20-30%,部署密度大大提升。
Stream API 增强
Stream.toList() 从 JDK 16 的预览变成了默认行为,Stream.gather() 在 JDK 23 第二次预览后继续完善,链式数据处理更优雅。
JDK 25:最新 LTS,String Templates 正式登场 ⭐⭐
是什么
JDK 25 于 2025 年 9 月发布,是目前最新的 LTS 版本(Oracle JDK 25 提供至 2031年的长期支持)。
String Templates 正式化(JEP 468)
这是继文本块之后,多行字符串处理的又一次飞跃:
// JDK 25 String Templates 正式版
String name = "张三";
int score = 95;
// STR 处理器支持嵌入表达式
String message = STR."Hello, {name}! Your score is {score}.";
// 多行模板
String html = STR."""
<div>
<h1>{title}</h1>
<p>{content}</p>
</div>
""";
模板表达式 {...} 可以嵌入任何 Java 表达式,运行时直接求值。比字符串拼接更安全(减少 SQL 注入风险),比 String.format 更直观。
Scoped Values(JEP 506 正式特性)
比 ThreadLocal 更好用的线程内共享机制,天然支持虚拟线程:
static final ScopedValue<String> USER = new ScopedValue<>();
// 设置值
ScopedValue.where(USER, "张三")
.run(() -> {
// 虚拟线程间共享,安全且高效
System.out.println(USER.get()); // 张三
});
紧凑源文件与实例 main 方法(JEP 512)
// 一个文件搞定 Hello World,不需要类名
void main() {
System.out.println("Hello, Java 25!");
}
JDK 26:HTTP/3 来了,G1 吞吐量暴涨
是什么
JDK 26 于 2026年3月17日 正式发布,是 JDK 25 之后的第一个非 LTS 版本,带来了 10 个 JEP,涵盖网络、性能、安全等多个维度。
HTTP/3 支持正式化(JEP 517)⭐
// 自动协商协议(推荐方式)
HttpClient client = HttpClient.newHttpClient();
// 明确指定 HTTP/3
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
基于 QUIC 协议(UDP),0-RTT 握手、消除队头阻塞、连接迁移(Wi-Fi 切移动数据不丢连接),高丢包网络下性能大幅提升。不用改一行业务代码,只需要升级 JDK,就能享受 HTTP/3 的速度提升。
G1 GC 吞吐量暴涨(JEP 522)⭐
JDK 26 对 G1 GC 的写屏障做了大手术,通过双卡表机制,将 x64 架构下的写屏障指令从约 50 条压缩到约 12 条:
| 场景 | 吞吐量提升 |
|---|---|
| 频繁修改引用字段 | +5%~15% |
| 不频繁修改引用 | +5% |
这对微服务、数据库连接池等引用操作密集型应用是重大利好。
AOT 对象缓存支持所有 GC(JEP 516)
这是 Project Leyden 的又一次里程碑。AOT 缓存现在可以用在任何 GC 上,包括 ZGC:
| 对比 | JDK 24(AOT 缓存) | JDK 26(AOT 缓存) |
|---|---|---|
| 支持 GC | 仅 G1 | 所有 GC(含 ZGC) |
| 训练阶段和生产阶段 | 必须同一种 GC | GC 可不同 |
这对 Serverless 和云原生应用意义重大——训练时用 G1,生产时用 ZGC,冷启动时间大幅缩短。
final 字段真正不可变(JEP 500)
JDK 26 开始,通过反射修改 final 字段会触发 JVM 警告:
// JDK 26+ 会发出警告
Field field = MyClass.class.getDeclaredField("NAME");
field.setAccessible(true);
field.set(null, "新值"); // ⚠️ Warning: final field mutation detected
为未来版本默认禁止此类操作做准备,Java 的完整性安全更进一步。
移除 Applet API(JEP 504)
Applet 这个上世纪的浏览器插件技术终于被彻底扫进历史垃圾堆,JDK 体积又小了一点。
一张图总结:JDK 9-26 特性选版指南
| 版本 | 类型 | 核心特性 | 减少样板代码量 |
|---|---|---|---|
| JDK 9 | 特性 | 模块系统、文本块预览 | ★★★★★ 多行字符串 |
| JDK 10 | 特性 | var 类型推断 | ★★★★☆ 局部变量 |
| JDK 11 | LTS | HTTP Client API、ZGC | ★★★★★ HTTP/并发 |
| JDK 12-13 | 预览迭代 | Switch 表达式、文本块 | ★★★★☆ 分支逻辑 |
| JDK 14-15 | 预览迭代 | Records、Sealed Classes | ★★★★★ POJO/继承控制 |
| JDK 16 | 特性 | Records 正式、instanceof 模式匹配 | ★★★★☆ 类型判断 |
| JDK 17 | LTS | Sealed Classes 正式、Switch 正式 | ★★★☆☆ 类族控制 |
| JDK 18-20 | 积累 | 虚拟线程预览(Loom)、UTF-8 默认 | ★★★★☆ 并发 |
| JDK 21 | LTS ⭐ | 虚拟线程正式、Sequenced Collections | ★★★★★ 并发/集合 |
| JDK 22 | 特性 | 外部函数 API、未命变量 | ★★★★☆ 原生互操作 |
| JDK 23 | 特性 | Markdown 文档、Stream Gatherers 预览 | ★★★☆☆ 文档/流处理 |
| JDK 24 | 特性 | 紧凑对象头、Stream API 增强 | ★★★☆☆ 内存优化 |
| JDK 25 | LTS ⭐ | String Templates 正式、Scoped Values | ★★★★★ 字符串/并发 |
| JDK 26 | 特性 | HTTP/3、G1 吞吐量优化 | ★★★★☆ 网络/GC |
总结:Java 正在变成你想不到的样子
写完这篇文章,我自己最大的感受是:Java 变年轻了。
从 JDK 8 到 JDK 26,这门语言没有吃老本,而是一直在进化:
var让你少写重复的类型名- Records 让你告别 Lombok 的黑魔法
- Sealed Classes 让你精确控制继承关系
- 虚拟线程 让你用同步写法写高性能并发程序
- String Templates 让你更安全地拼接字符串
- HTTP/3 让你不动代码就能提升网络性能
如果你的团队还在用 JDK 8,不妨先从 var 和 Records 入手,感受一下现代 Java 的简洁。等 JDK 21 或 JDK 25 的 LTS 稳定了,虚拟线程 + String Templates 绝对值得迁移。
你们公司现在用的是什么 JDK 版本?有没有哪个新特性用了就回不去的?评论区聊聊 👇