代码的艺术:从 JDK 8 的函数式狂潮到 JDK 17 的现代优雅
- JDK 8 引入了 Lambda 表达式、Stream API 和 Optional 类,这些特性极大地增强了 Java 的函数式编程能力,简化了代码编写并提高了处理集合数据的效率。
- JDK 9 推出了模块化系统(JPMS)和 JShell,前者通过更好的依赖管理和封装提升了大型应用的性能与安全性;后者提供了一个交互式的 Java 环境,便于快速测试代码片段。
- JDK 10 引入局部变量类型推断 (
var),使得代码更加简洁的同时保持了类型安全。 - JDK 11 包含了新的 HTTP Client API 和实验性的 ZGC 垃圾收集器,提供了更现代且高效的网络请求处理方式及内存管理方案。
- 从 JDK 12 到 JDK 17,增加了 Switch 表达式、文本块、Record 类型以及 Sealed Classes 等功能,进一步丰富了语言特性,使 Java 编程更加灵活高效。特别是 JDK 17 作为一个 LTS 版本,整合了之前版本的所有改进,为开发者提供了一个稳定可靠的开发平台。
为了便于阅读,本文将分为以下几个部分:
- JDK 8:流(Stream)、函数式接口、默认方法、Optional 类
- JDK 9:模块化系统(Jigsaw)、JShell
- JDK 10:局部变量类型推断(var)
- JDK 11:HTTP Client、ZGC 垃圾收集器(实验性)
- JDK 12 & 13:Switch 表达式、Text Blocks(文本块)
- JDK 14 & 15:Record 类型、Sealed Classes(密封类)
- JDK 16:Record、Sealed Classes(最终版本)
- JDK 17:LTS 版本,主要特性为前面版本的累积和稳定
JDK 8:Java 的“文艺复兴”
JDK 8 是 Java 历史上最重要的一次版本更新,引入了大量现代编程范式,彻底改变了 Java 的编写方式。
1. Lambda 表达式与函数式接口
Lambda 表达式让 Java 能够以更简洁的语法实现匿名函数,核心是函数式接口。一个函数式接口是指只有一个抽象方法的接口,例如 Runnable、Callable。
官方文档: Lambda Expressions
代码示例:
Java
// 传统写法:匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from a thread!");
}
}).start();
// JDK 8 Lambda 写法
new Thread(() -> System.out.println("Hello from a lambda thread!")).start();
2. Stream API
Stream API 提供了一种全新的方式来处理集合数据,它支持声明式编程,让你能够像 SQL 一样对集合进行“查询”。Stream 本身是不可变的,并且是懒加载的。
官方文档: Stream API
代码示例:
Java
List<String> names = Arrays.asList("Tom", "Jerry", "Spike", "Tom");
// 筛选出以 "T" 开头的,转换成大写,去重,并打印
names.stream()
.filter(name -> name.startsWith("T")) // 筛选
.map(String::toUpperCase) // 转换
.distinct() // 去重
.forEach(System.out::println); // 遍历打印
3. Optional 类
Optional<T> 是一个容器对象,可以包含或不包含一个非 null 值。它的主要目的是解决空指针异常(NullPointerException),鼓励你以更函数式的方式来处理可能为空的值。
官方文档: Optional Class
代码示例:
Java
String name = "John";
String nullName = null;
Optional<String> optionalName = Optional.ofNullable(name);
Optional<String> optionalNullName = Optional.ofNullable(nullName);
// 使用 orElse,如果值不存在,则提供一个默认值
String result1 = optionalName.orElse("Default"); // "John"
String result2 = optionalNullName.orElse("Default"); // "Default"
// 使用 ifPresent,当值存在时执行一个操作
optionalName.ifPresent(n -> System.out.println("Hello, " + n));
JDK 9:模块化与 JShell
JDK 9 的核心是 Project Jigsaw,旨在解决大型应用中依赖管理混乱的问题,并提高性能。
1. 模块化系统(JPMS)
Java Platform Module System (JPMS) 允许你将代码组织成独立的模块。每个模块都有一个 module-info.java 文件,明确声明它依赖哪些模块,以及它对外暴露哪些包。
官方文档: Java Platform Module System
2. JShell
JShell 是一个交互式的 Java REPL (Read-Eval-Print Loop) 工具。你可以直接在命令行中输入 Java 代码片段并立即执行,无需编写完整的 main 方法。这对于学习 Java 语法或测试代码片段非常有用。
官方文档: JShell Guide
代码示例 (在命令行中):
Bash
> jshell
| Welcome to JShell -- Version 9
| For an introduction type: /help intro
jshell> int sum = 10 + 20;
sum ==> 30
jshell> System.out.println("Hello, JShell!");
Hello, JShell!
JDK 10:局部变量类型推断(var)
var 关键字允许你在声明局部变量时,让编译器根据初始化值自动推断其类型。这能让代码更简洁,但要注意不能用于方法参数、返回值或成员变量。
官方文档: JEP 286: Local-Variable Type Inference
代码示例:
Java
// JDK 9 及之前
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
// JDK 10 以后
var myMap = new HashMap<String, List<String>>();
JDK 11:新 HTTP Client 和 ZGC
JDK 11 是一个 LTS (Long-Term Support) 版本,带来了生产就绪的 HTTP Client 和实验性的 ZGC。
1. HTTP Client
JDK 11 中正式引入了新的 java.net.http.HttpClient,它提供了更现代、更易用的 API 来处理 HTTP 请求,支持同步和异步调用。
官方文档: HTTP Client
代码示例:
Java
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/get"))
.GET() // 或 .POST(...)
.build();
// 同步发送请求
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
JDK 12 & 13:Switch 表达式与文本块
1. Switch 表达式
Switch 表达式允许 switch 语句作为表达式使用,并可以直接返回值,消除了传统 switch 中的 break 语句,避免了“穿透”问题。
官方文档: JEP 361: Switch Expressions (Standard)
代码示例:
Java
// JDK 13 以后
String day = "MONDAY";
String result = switch (day) {
case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Workday";
case "SATURDAY", "SUNDAY" -> "Weekend";
default -> throw new IllegalArgumentException("Invalid day: " + day);
};
System.out.println(result);
2. Text Blocks(文本块)
文本块提供了多行字符串的支持,使用三个双引号 """ 包围,无需手动转义换行符和引号,极大地简化了多行字符串(如 JSON、SQL)的编写。
官方文档: JEP 378: Text Blocks (Standard)
代码示例:
Java
// 传统多行字符串
String jsonOld = "{n" +
" "name": "John",n" +
" "age": 30n" +
"}";
// 文本块
String jsonNew = """
{
"name": "John",
"age": 30
}
""";
System.out.println(jsonNew);
JDK 14 & 15:Record 与 Sealed Classes
1. Record(记录)
Record 是一种特殊的类,用于创建不可变的数据载体。它自动生成构造函数、访问器、equals()、hashCode() 和 toString() 方法,省去了大量样板代码。
官方文档: JEP 395: Records (Standard)
代码示例:
Java
// 传统数据类
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
//… getter, equals, hashCode, toString…
}
// Record 写法
public record Point(int x, int y) {}
// 使用
Point p1 = new Point(10, 20);
System.out.println(p1.x()); // 访问器自动生成
System.out.println(p1); // toString() 自动生成
2. Sealed Classes(密封类)
Sealed Classes 允许你限制哪些类可以继承一个类或实现一个接口。你使用 sealed 关键字声明一个类,并用 permits 关键字指定允许继承的子类。这为面向对象设计提供了更精细的控制。
官方文档: JEP 409: Sealed Classes (Standard)
代码示例:
Java
// Shape 是一个密封接口,只允许 Circle 和 Square 实现
public sealed interface Shape permits Circle, Square {}
final class Circle implements Shape {}
final class Square implements Shape {}
// Polygon 不能实现 Shape,编译器会报错
// class Polygon implements Shape {}
JDK 16 & 17:最终定型与 LTS
JDK 16 将 Record 和 Sealed Classes 这两个特性最终定型,使其可以用于生产环境。
JDK 17 作为最新的 LTS 版本,将上述所有新特性(如 Record、Sealed Classes、Text Blocks 等)整合并稳定下来。对于企业级应用来说,JDK 17 是一个非常推荐的升级目标,因为它在性能、稳定性和功能上都达到了一个新的高度。
希望这篇文章能帮助你快速了解 JDK 8 到 JDK 17 的演进过程。这些新特性大大提高了 Java 的开发效率和代码质量,值得你在项目中积极尝试。