引言
Java 作为企业级应用的基石,其每一个长期支持(LTS)版本都标志着一次重要的技术演进。JDK 8 于 2014 年发布,因其引入的 Lambda 表达式和 Stream API 等革命性特性,至今仍是生产环境中使用最广泛的版本。而近十年后,2023 年发布的 JDK 21 作为最新的 LTS 版本,则代表了现代 Java 的新高度。
本文将带你深入探索从 JDK 8 到 JDK 21 的巨大变化,这不仅是一次版本的升级,更是一次开发范式、性能表现和语言表达能力的全面飞跃。
一、 核心定位与发布模式的变化
- JDK 8 (2014): 最后一个采用传统“特性驱动”发布模式的 LTS 版本。它的成功源于其划时代的新特性,使其成为了一个“现象级”版本。
- JDK 21 (2023): 采用 “六个月一次” 的快速发布模式后的第三个 LTS 版本。这种模式意味着新特性可以更快地交付到开发者手中,JDK 21 是多年来持续创新的集大成者,稳定性经过了多个版本的打磨。
二、 革命性新特性:JDK 8 的基石
JDK 8 的核心贡献是引入了函数式编程能力,彻底改变了 Java 的编码风格。
-
Lambda 表达式
- 允许将函数作为方法参数传递,极大地简化了代码。
(parameters) -> expression
或(parameters) -> { statements; }
-
Stream API
- 用于处理集合数据的声明式、函数式 API。支持顺序和并行操作,简化了集合的过滤、映射、归约等复杂操作。
- 示例:从列表中找出所有偶数并求和。
// JDK 8 之前 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = 0; for (int num : numbers) { if (num % 2 == 0) { sum += num; } } // JDK 8 Stream API int sum = numbers.stream() .filter(n -> n % 2 == 0) .mapToInt(Integer::intValue) .sum();
-
默认方法
- 允许在接口中添加具有实现的方法,使用
default
关键字。这是为了支持库的演进,例如在Collection
接口中添加新方法而不破坏所有现有实现。
- 允许在接口中添加具有实现的方法,使用
-
新的日期时间 API
- 引入了
java.time
包,提供了不可变、线程安全的LocalDate
,LocalTime
,Instant
,Period
,Duration
等类,彻底解决了旧Date
和Calendar
类的设计缺陷。
- 引入了
三、 迈向现代Java:JDK 9 及以后的关键特性(均在JDK21中可用)
JDK 21 包含了自 JDK 9 以来所有的重要特性,以下是其中最突出的部分:
-
模块系统
- 引入于 JDK 9:这是 Java 平台的一次架构级升级。通过
module-info.java
文件声明模块的依赖和导出包,实现了强封装性和可靠的配置。它解决了 JAR Hell 问题,并允许构建更小、更安全的运行时镜像(通过 jlink 工具)。
- 引入于 JDK 9:这是 Java 平台的一次架构级升级。通过
-
垃圾回收器的演进
- G1GC (JDK 9+ 的默认GC):在 JDK 9 中取代 Parallel GC 成为默认垃圾回收器,旨在平衡延迟和吞吐量。
- ZGC 和 Shenandoah:JDK 11 和 15 中引入的低延迟垃圾回收器。它们的目标都是将停顿时间控制在毫秒级,甚至亚毫秒级,且停顿时间不会随堆大小增长而增加。这对于大型内存应用和需要高响应性的服务至关重要。JDK 21 中,ZGC 的功能已非常成熟稳定。
-
语言语法增强
var
局部变量类型推断:引入于 JDK 10。允许开发者省略明显的类型声明,使代码更简洁,同时保持编译时的强类型检查。// JDK 10+ var list = new ArrayList<String>(); // list is of type ArrayList<String>
switch
表达式:引入于 JDK 14。switch
可以直接返回值,并且通过->
箭头语法避免了繁琐的break
语句,大大减少了错误。// JDK 14+ String dayType = switch (day) { case "MON", "TUE", "WED", "THU", "FRI" -> "Weekday"; case "SAT", "SUN" -> "Weekend"; default -> throw new IllegalArgumentException("Invalid day: " + day); };
- 文本块:引入于 JDK 15。使用三重引号
"""
来定义多行字符串,完美处理 JSON、HTML、SQL 等格式的字符串,无需再使用大量的转义和连接符。// JDK 15+ String json = """ { "name": "张三", "age": 30 } """;
- Record 类:引入于 JDK 16。一种透明的数据载体,用于简化不可变数据的建模。编译器会自动生成构造函数、getter、
equals()
、hashCode()
和toString()
方法。// JDK 16+ public record Person(String name, int age) { } // 使用 Person p = new Person("Alice", 30); System.out.println(p.name()); // Alice
- 密封类:引入于 JDK 17。允许类或接口明确规定哪些其他类或接口可以扩展或实现它们。这是一种更强、更精确的继承控制,是实现模式匹配的关键基石。
// JDK 17+ public sealed abstract class Shape permits Circle, Rectangle, Square { ... } public final class Circle extends Shape { ... } public non-sealed class Rectangle extends Shape { ... }
四、 JDK 21 的里程碑:虚拟线程
这是 JDK 21 最引人注目的特性,旨在彻底解决 Java 高并发应用的瓶颈。
- 背景:平台线程(传统线程)与操作系统线程是 1:1 映射的。创建大量线程会消耗大量内存(栈内存)和CPU调度资源。
- 虚拟线程:由 JVM 管理的轻量级线程。它与操作系统线程是 M:N 映射关系。创建数百万个虚拟线程是可行的,因为它们开销极小(初始内存仅约 1KB)。
- 意义:允许开发者使用同步的、阻塞的代码风格(简单易懂)来编写高并发应用,同时却能获得异步非阻塞的性能。这极大地提高了并发应用的吞吐量,同时降低了编写和调试的复杂性。
- 使用:非常简单,API 与传统线程兼容。
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i -> { executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return i; }); }); } // 这里会等待所有虚拟线程执行完毕
五、 总结与对比表格
特性类别 | JDK 8 (2014) | JDK 21 (2023) | 演进意义 |
---|---|---|---|
发布模式 | 传统模式 | 6个月发布周期 | 新特性交付更快,质量更稳定 |
编程范式 | 引入函数式编程 | 函数式 + 声明式 + 数据导向 | 代码更简洁,表达力更强 |
项目架构 | 类路径 | 模块化 | 强封装性,减少冲突,构建更小镜像 |
并发模型 | 平台线程 | 虚拟线程 | 极大提升并发吞吐量,简化并发编程 |
垃圾回收 | Parallel GC (默认) | G1GC (默认), ZGC/Shenandoah | 停顿时间极短,适合低延迟应用 |
API 丰富度 | 基础 | 大量新API | 更现代、更易用的日期时间、HTTP Client等 |
语法糖 | Lambda, Stream | var, switch表达式, 文本块, Record, 密封类 | 极大减少模板代码,提升开发效率和安全 |
六、 如何选择与升级建议
- 为什么仍用 JDK 8? 历史项目庞大,迁移成本高;依赖的第三方库可能尚未支持高版本;系统稳定,没有新特性需求。
- 为什么应该升级到 JDK 21?
- 性能提升:尤其是GC和虚拟线程带来的巨大性能收益。
- 开发效率:现代语言特性能让你写更少、更清晰、更安全的代码。
- 安全与支持:Oracle 已于 2030 年 12 月停止对 JDK 8 的免费商业支持,继续使用将面临安全风险。JDK 21 作为最新 LTS,将获得长期支持。
- 未来Proof:新框架和库(如 Spring 6+)已基于 JDK 17+ 构建,拥抱未来技术栈。
升级步骤建议:
- 评估:使用
jdeprscan
工具扫描代码中使用的已过时 API。 - 测试:在测试环境中使用 JDK 21 编译和运行应用。
- 逐步迁移:对于大型项目,可以考虑先升级到 JDK 11(另一个重要LTS),再逐步升级到 JDK 21。
- 拥抱新特性:逐步尝试使用
var
、switch
表达式等低风险特性,然后评估使用 Record 和虚拟线程重构部分代码的可能性。
结语
从 JDK 8 到 JDK 21,Java 并没有停下脚步,而是进行了一场深刻的自我革新。它变得更快、更轻、更简单、更强大。对于开发者而言,升级不仅仅是更换一个运行环境,更是拥抱一种更现代、更高效的编程体验和思维方式。JDK 21 无疑是为未来十年准备的Java新基石,是时候开始你的升级之旅了!