一句话省流
JDK 8 的 Lambda/Stream 让你写出更简洁的代码;JDK 17 的 Record 消灭 DTO 的 getter/setter;JDK 21 的虚拟线程让高并发 I/O 性能飙升;JDK 25 的紧凑对象头能节省 30% 内存。
📌 快速查阅(点击跳转)
- JDK 版本怎么选?一张表搞定
- JDK 7:那些你可能不知道的语法糖
- JDK 8:改变 Java 命运的一次升级
- JDK 11:首个“小而美”的 LTS
- JDK 17:现代 Java 开发的标准答案
- JDK 21:并发编程的革命
- JDK 25:性能与 AI 时代的 LTS
- Spring Boot 各版本该配哪个 JDK?
- 升级路线图 + 最值得学的 5 个特性
📌 JDK 版本怎么选?一张表搞定
生产环境原则:只用 LTS 版本(8、11、17、21、25)。非 LTS 版本寿命仅 6 个月,不适合线上。
| 版本 | 发布年 | 是否 LTS | 定位 | 生产建议 |
|---|---|---|---|---|
| 7 | 2011 | ❌ | 语法糖补全 | 已淘汰 |
| 8 | 2014 | ✅ | 函数式革命 | 存量项目主流,新项目不推荐 |
| 11 | 2018 | ✅ | 小特性 LTS | 过渡版本 |
| 17 | 2021 | ✅ | x现代 Java 基石 | 当前新建项目首选 |
| 21 | 2023 | ✅ | 高并发大杀器 | 强烈推荐 I/O 密集型 |
| 25 | 2025 | ✅ | 性能 + AI 增强 | 面向未来,适合云原生 |
🚀 JDK 7 (2011):那些你可能不知道的语法糖
JDK 7 虽然常被忽略,但它引入的几个特性每天都在被无数 Java 开发者使用。
1️⃣ try-with-resources —— 告别 finally 里关流
痛点:以前操作文件/数据库连接,必须在 finally 里手动 close(),代码冗长还容易漏掉。
// ❌ 旧写法(JDK 6 及以前)
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("data.txt"));
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try { br.close(); } catch (IOException e) { e.printStackTrace(); }
}
}
// ✅ JDK 7+:自动关闭,清爽到哭
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
// 多个资源用分号隔开,关闭顺序与声明顺序相反
实战注意:只有实现了 AutoCloseable 或 Closeable 的对象才能这样用。Java 7 之后,所有 I/O 流、JDBC 的 Connection、Statement、ResultSet 都支持。
一个隐藏优化:如果 try 块和 close() 都抛出异常,close() 的异常会被抑制,可通过 Throwable.getSuppressed() 获取。
2️⃣ 菱形操作符 <> —— 少写一半泛型代码
// ❌ 以前
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
// ✅ 现在
Map<String, List<Integer>> map = new HashMap<>();
注意:JDK 7 只支持在声明时用 <>,JDK 9 之后才允许匿名内部类中使用。
3️⃣ 数字字面量下划线 —— 一眼看出是多少钱
int balance = 10_000_000; // 一千万
long cardNo = 1234_5678_9012_3456L;
double pi = 3.1415_9265;
约定:通常每三位一组(金额)、每四位一组(卡号)、每两位一组(字节)。
🔥 JDK 8 (2014):改变 Java 命运的一次升级
JDK 8 是 Java 历史上最重要的版本,没有之一。下面几个特性彻底改变了我们的编码方式。
1️⃣ Lambda 表达式 —— 让代码成为数据
背景:以前传递一段行为必须用匿名内部类,啰嗦得让人不想写。
// ❌ 匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
}).start();
// ✅ Lambda(一行搞定)
new Thread(() -> System.out.println("Hello")).start();
语法速记:
- 无参数:
() -> 表达式 - 单参数(可省略类型):
x -> x*2 - 多参数:
(a,b) -> a + b - 多行语句必须用
{}并写return。
闭包陷阱:Lambda 引用的局部变量必须是 final 或 effectively final(即赋值后不再改变)。不要试图在 Lambda 内部修改外部循环变量。
2️⃣ Stream API —— 集合操作的“流水线”
Stream 让你用声明式的方式处理集合,代码可读性暴增。
// 需求:获取年龄>18的用户,按年龄倒序,取前3个,只留姓名
List<String> result = users.stream()
.filter(u -> u.getAge() > 18)
.sorted(Comparator.comparingInt(User::getAge).reversed())
.limit(3)
.map(User::getName)
.collect(Collectors.toList());
效率提示:
stream()是串行,parallelStream()是并行(大数据集才有收益)。- 尽量避免在
filter之后立即sorted,那样会破坏并行性。 - 终止操作(
collect、forEach、reduce)才会真正执行,中间操作是惰性的。
3️⃣ Optional —— 拯救 null 指针
痛点:NullPointerException 是 Java 的头号克星。
// ❌ 链式调用遇到 null 就爆炸
String city = user.getAddress().getCity();
// ✅ 优雅处理
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
最佳实践:
- ✅ 只作为返回值,不要用作方法参数或字段。
- ✅ 用
orElseGet(Supplier)代替orElse来延迟执行(避免不必要的开销)。 - ❌ 别调用
Optional.get()而不检查isPresent()。
4️⃣ 新的日期时间 API —— 别再忍受 Date 了
java.util.Date 的设计可谓反人类:月份从 0 开始、线程不安全、时区混乱。
// ❌ 旧方式
Calendar cal = Calendar.getInstance();
cal.set(2024, 0, 15); // 1月要写0,醉了
Date date = cal.getTime();
// ✅ 新方式
LocalDate date = LocalDate.of(2024, Month.JANUARY, 15);
LocalDateTime now = LocalDateTime.now();
LocalDate nextWeek = LocalDate.now().plusWeeks(1);
Period age = Period.between(birthday, LocalDate.now());
注意:LocalDateTime 不带时区,如果需要带时区用 ZonedDateTime;数据库交互建议用 Instant。
⚡ JDK 11 (2018):首个“小而美”的 LTS
JDK 11 重在稳定和增强,没有大刀阔斧的语法改动,但有几个生产力特性值得记住。
1️⃣ 直接运行 .java 文件
java Hello.java # 无需 javac 编译,适合写脚本或快速测试
适用场景:写一次性工具、教学演示、快速验证算法。不会产生 .class 文件。
2️⃣ HTTP Client —— 内置的现代化客户端
以前我们要用 Apache HttpClient 或 OkHttp,现在 JDK 自带。
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/octocat"))
.timeout(Duration.ofSeconds(10))
.header("Accept", "application/json")
.GET()
.build();
// 同步请求
HttpResponse<String> resp = client.send(request, HttpResponse.BodyHandlers.ofString());
// 异步请求(返回 CompletableFuture)
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(resp -> System.out.println(resp.body()));
优势:原生支持 HTTP/2、WebSocket、异步非阻塞。
3️⃣ var —— 局部变量类型推断(实际 JDK 10 引入,11 稳定)
var map = new HashMap<String, List<Integer>>(); // 推断为 HashMap<...>
var list = List.of(1, 2, 3);
⚠️ 三个不能用的地方:
- 方法参数和返回值
- 成员变量
- 赋值为
null(无法推断)
建议:只在类型名明显冗余时使用(如 new HashMap<>()),不要滥用导致可读性下降。
4️⃣ 集合不可变工厂方法
List<String> list = List.of("A", "B", "C"); // 不可变
list.add("D"); // 抛出 UnsupportedOperationException
适用于:常量集合、配置项。
🛡️ JDK 17 (2021):现代 Java 开发的标准答案
JDK 17 汇集了 JDK 9~16 中预览多年的特性,正式成为企业级开发的“新基线”。
1️⃣ Record —— 杀死 POJO 样板代码
一个 DTO 需要构造器、getter、equals、hashCode、toString 几十行代码?Record 一行搞定。
public record User(Long id, String name, Integer age) {}
// 使用
User user = new User(1L, "张三", 25);
System.out.println(user.name()); // 注意:不是 getName()
System.out.println(user); // User[id=1, name=张三, age=25]
高级用法:添加验证逻辑。
public record Product(String name, BigDecimal price) {
public Product { // 紧凑构造器
if (price.compareTo(BigDecimal.ZERO) < 0)
throw new IllegalArgumentException("价格不能为负");
}
}
Record vs Lombok:Record 无外部依赖、不可变,适合作为数据传输对象(DTO)、API 返回对象;Lombok 更灵活但需要插件。
2️⃣ Switch 表达式 —— 终于不用写 break 了
// ✅ 箭头 + 表达式返回
String type = switch (day) {
case 1, 2, 3, 4, 5 -> "工作日";
case 6, 7 -> "周末";
default -> throw new IllegalArgumentException();
};
// 需要多行语句时用 yield
String result = switch (value) {
case 1 -> "一";
default -> {
System.out.println("其他");
yield "未知";
}
};
模式匹配(JDK 21 正式):
Object obj = "Hello";
String desc = switch (obj) {
case Integer i -> "整数 " + i;
case String s && s.length() > 5 -> "长字符串";
case String s -> "短字符串";
case null -> "null值";
default -> "未知";
};
3️⃣ 文本块 —— 告别转义地狱
写 JSON、SQL、HTML 时,无需再加一堆 \n 和引号转义。
String json = """
{
"name": "Java 17",
"features": ["文本块", "Record"]
}
""";
注意:文本块的开头的 """ 后面不能直接跟非空白字符,否则会编译错误。
🧵 JDK 21 (2023):并发编程的革命
JDK 21 最大的亮点是虚拟线程,它让 Java 在处理高并发 I/O 时的性能直追 Go 和 Erlang。
1️⃣ 虚拟线程 —— 百万并发不是梦
传统线程的痛:每个 Java 线程对应一个 OS 线程,占用约 1MB 栈内存,创建销毁成本高。一旦并发数上万,系统就不堪重负。
虚拟线程:由 JVM 管理的轻量线程,一个 OS 线程可以挂载成千上万个虚拟线程,阻塞时自动让出载体线程,切换开销极低。
// 创建虚拟线程(JDK 21)
Thread vThread = Thread.ofVirtual().start(() -> {
System.out.println("我是虚拟线程");
});
// 生产级用法:虚拟线程池
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 100_000; i++) {
executor.submit(() -> handleRequest());
}
} // 自动等待所有任务完成
何时使用:
- ✅ I/O 密集型:数据库查询、远程调用、文件读写 → 效果爆炸
- ❌ CPU 密集型:密集计算任务 → 仍用传统线程池
避坑:不要在虚拟线程中使用 synchronized 块,它会固定载体线程,导致性能下降。建议用 ReentrantLock 替代。
2️⃣ 结构化并发 —— 让并发代码像同步一样明确
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Subtask<String> user = scope.fork(() -> fetchUser());
Subtask<Integer> order = scope.fork(() -> fetchOrders());
scope.join(); // 等待所有子任务
scope.throwIfFailed(); // 任一失败则抛出异常
return new Result(user.get(), order.get());
} // 退出作用域时,未完成的任务自动取消
好处:线程生命周期与代码块绑定,防止线程泄漏,错误处理自然。
🌟 JDK 25 (2025):性能与 AI 时代的 LTS
JDK 25 重点在性能优化和现代化密码学支持。
1️⃣ 紧凑对象头 —— 减少 30% 内存占用
java -XX:+UseCompactObjectHeaders -jar app.jar
原理:传统 JVM 对象头占 12~16 字节(压缩指针下),JDK 25 将其压缩到 8 字节。对于缓存海量对象的系统(比如 Session、商品 SKU),内存占用显著下降,GC 次数减少。
2️⃣ 灵活构造器体 —— 终于可以在 super() 前写验证了
public class PositiveNumber extends Number {
PositiveNumber(int value) {
if (value <= 0) { // JDK 25 允许在 super 前执行验证
throw new IllegalArgumentException("必须为正数");
}
super(value);
}
}
之前只能写在 super() 后面,那时对象已经部分构造,不太优雅。
3️⃣ 基本类型模式匹配(预览)
Object obj = 42L;
switch (obj) {
case int x -> System.out.println("int: " + x);
case long l -> System.out.println("long: " + l);
default -> System.out.println("其他");
}
不再需要手动拆箱,switch 直接识别原始类型。
📌 Spring Boot 各版本该配哪个 JDK?
| Spring Boot 版本 | 最低 JDK | 兼容 JDK | 关键说明 |
|---|---|---|---|
| 2.x (2.4~2.7) | 8 | 8~17 | 2023 年底已停止维护,新项目慎用 |
| 3.0~3.4 | 17 | 17~23 | 强制 JDK 17+,改用 Jakarta EE |
| 3.4+ | 17 | 17~23 | 支持虚拟线程 (Tomcat 开启 spring.threads.virtual.enabled=true) |
| 4.0 (2025.11) | 17 | 17~25 | 原生镜像优先,Jakarta EE 11 |
升级决策:
- 存量 JDK 8 + Boot 2.x → 一步到位升级到 JDK 21 + Boot 3.4,同时获得虚拟线程 + Record + 文本块。
- 新建项目 → JDK 21 + Spring Boot 3.4(除非公司强制要求 17)。
- 追求极致启动速度和内存 → JDK 25 + Spring Boot 4.0 + GraalVM 原生镜像。
💡 升级路线图 + 最值得学的 5 个特性
升级难度评估
| 当前版本 | 推荐目标 | 难度 | 主要工作量 |
|---|---|---|---|
| JDK 7 | JDK 17 | ⭐⭐⭐⭐⭐ | 语法变更 + 第三方库兼容 |
| JDK 8 | JDK 21 | ⭐⭐⭐⭐ | 模块化可能引入的问题,但大部分代码兼容 |
| JDK 11 | JDK 21 | ⭐⭐ | 顺滑升级,注意 var 和新的 GC 参数 |
| JDK 17 | JDK 25 | ⭐ | 开启实验性参数即可享受到对象头压缩 |
新特性影响力 TOP 5(实用角度)
| 排名 | 特性 | 版本 | 一句话价值 |
|---|---|---|---|
| 🥇 | Lambda + Stream | 8 | 让你的集合操作代码减少 70% |
| 🥈 | 虚拟线程 | 21 | 高并发 Web 服务吞吐量提升 10 倍 |
| 🥉 | Record | 17 | 消灭 DTO 所有模板代码 |
| 4 | Switch 表达式 + 模式匹配 | 17/21 | 写出更安全的业务分支 |
| 5 | 新日期时间 API | 8 | 告别 Date 和 Calendar 的坑 |