以下是 JDK 8 至 JDK 21 中最具实用性的新特性整理,涵盖语言特性、工具类增强、性能优化等方向,附代码示例和注释说明:
一、JDK 8(2014):函数式编程与现代化API
JDK 8 是 Java 发展的里程碑版本,引入了大量颠覆性特性,至今仍是企业级项目的基础。
- Lambda 表达式
简化匿名内部类,支持函数式编程,使代码更简洁。
示例:用 Lambda 实现 Runnable 和 Comparator
// 传统匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("传统方式");
}
}).start();
// Lambda 表达式(简化版)
new Thread(() -> System.out.println("Lambda 方式")).start();
// 传统 Comparator 排序
List
Collections.sort(list, new Comparator
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Lambda 排序(更简洁)
Collections.sort(list, (a, b) -> a.compareTo(b));
// 或直接使用方法引用:list.sort(String::compareTo);
- Stream API
提供声明式集合操作(过滤、映射、聚合等),支持并行计算。
示例:用 Stream 处理集合
List
// 过滤偶数,平方后求和
int sum = numbers.stream()
.filter(n -> n % 2 == 0) // 过滤偶数
.mapToInt(n -> n * n) // 平方
.sum(); // 求和
System.out.println(sum); // 输出:2² + 4² + 6² = 4 + 16 + 36 = 56
- Optional 类
解决空指针异常(NPE),显式处理可能为空的值。
示例:避免空指针
public class OptionalDemo {
public static String getUsername(User user) {
// 如果 user/user.getName() 为 null,返回 "未知用户"
return Optional.ofNullable(user) // 包装可能为空的对象
.map(User::getName) // 提取 name(若 user 非空)
.orElse("未知用户"); // 最终默认值
}
public static void main(String[] args) {User user = new User("张三");User nullUser = null;System.out.println(getUsername(user)); // 输出:张三System.out.println(getUsername(nullUser));// 输出:未知用户
}static class User {private String name;public User(String name) { this.name = name; }public String getName() { return name; }
}
}
- 新的日期时间 API(java.time)
替代老旧的 Date 和 SimpleDateFormat,线程安全且设计更合理。
示例:日期时间操作
// 获取当前日期(不含时间)
LocalDate today = LocalDate.now();
System.out.println("今天:" + today); // 输出:2025-09-17
// 解析日期字符串
LocalDate birthday = LocalDate.parse("1990-05-20");
System.out.println("生日:" + birthday); // 输出:1990-05-20
// 计算两个日期的间隔(天数)
long daysBetween = ChronoUnit.DAYS.between(birthday, today);
System.out.println("距离生日已过:" + daysBetween + "天");
// 时间操作(带时区)
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("上海时间:" + zonedDateTime); // 输出:2025-09-17T16:45:00+08:00[Asia/Shanghai]
二、JDK 9(2017):模块化与基础增强
JDK 9 引入了模块化系统(JPMS),并对核心类库做了多项实用改进。
- 模块化系统(JPMS)
通过 module-info.java 明确模块依赖,提升代码封装性和安全性。
示例:模块定义与依赖
// 模块描述文件:module-info.java
module com.example.app {
requires java.base; // 依赖基础模块(所有模块隐式依赖)
requires com.example.utils; // 显式依赖自定义工具模块
exports com.example.app.api; // 导出包供其他模块使用
}
// 模块内代码(com/example/app/api/Calculator.java)
package com.example.app.api;
public class Calculator {
public int add(int a, int b) { return a + b; }
}
- 不可变集合工厂方法
List、Set、Map 新增静态工厂方法(如 of()、ofEntries()),快速创建不可变集合。
示例:创建不可变集合
// 不可变 List(线程安全,无法添加/删除元素)
List
// immutableList.add("d"); // 抛出 UnsupportedOperationException
// 不可变 Set(无重复元素)
Set
// 不可变 Map(键值对固定)
Map<String, Integer> immutableMap = Map.of("one", 1, "two", 2);
// 若需多个键值对,使用 Map.ofEntries()
Map<String, Integer> map = Map.ofEntries(
entry("three", 3),
entry("four", 4)
);
- Stream 增强
新增 takeWhile()、dropWhile()(按条件截断流)和 ofNullable()(处理空值)。
示例:Stream 截断与空值处理
List
// takeWhile:遇到第一个不满足条件的元素后停止处理(顺序流)
numbers.stream()
.takeWhile(n -> n % 2 == 0) // 取前三个偶数(2,4,6)
.forEach(System.out::print); // 输出:246
// dropWhile:跳过前几个满足条件的元素,直到遇到不满足的(顺序流)
numbers.stream()
.dropWhile(n -> n < 7) // 跳过 2,4,6(都小于7),从7开始
.forEach(System.out::print); // 输出:7810
// ofNullable:避免空流
Stream
stream.findFirst().ifPresent(System.out::println); // 无输出(流为空)
三、JDK 10(2018):局部变量类型推断
JDK 10 引入 var 关键字,简化局部变量声明(仅适用于局部变量,不可用于方法参数或字段)。
var 局部变量类型推断
编译器自动推断变量类型,减少冗余代码。
示例:用 var 简化代码
public class VarDemo {
public static void main(String[] args) {
// 传统声明
String name = "张三";
List
// 使用 var(类型由编译器推断)var nameInferred = "张三"; // 推断为 Stringvar numbersInferred = new ArrayList<>(); // 推断为 ArrayList<Object>(需注意类型安全)var list = List.of(1, 2, 3); // 推断为 List<Integer>// 注意:var 不能用于方法参数或返回值类型// public var method(var param) {} // 编译错误
}
}
四、JDK 11(2018):HTTP Client 与字符串增强
JDK 11 是长期支持(LTS)版本,新增 HTTP/2 客户端、字符串工具方法等。
- HTTP Client(正式版)
替代传统的 HttpURLConnection,支持 HTTP/1.1 和 HTTP/2,异步非阻塞。
示例:发送 HTTP GET 请求
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;
public class HttpClientDemo {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
// 同步请求HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://httpbin.org/get")).build();HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());System.out.println("状态码:" + response.statusCode()); // 输出:200System.out.println("响应体:" + response.body().substring(0, 50) + "..."); // 输出部分内容// 异步请求(返回 CompletableFuture)CompletableFuture<HttpResponse<String>> asyncResponse = client.sendAsync(request,HttpResponse.BodyHandlers.ofString());asyncResponse.thenAccept(res -> System.out.println("异步响应状态码:" + res.statusCode()));
}
}
- 字符串新增方法
String 类新增 isBlank()、strip()(去除首尾空白)、repeat() 等方法。
示例:字符串操作
String str1 = " \t hello \n ";
String str2 = "";
String str3 = "Java";
System.out.println(str1.isBlank()); // 输出:false(包含非空白字符)
System.out.println(str2.isBlank()); // 输出:true(空字符串或全空白)
System.out.println(str1.strip()); // 输出:"hello"(去除首尾空白)
System.out.println(str1.trim()); // 输出:"hello"(传统方法,不处理 \t/\n)
System.out.println(str3.repeat(3)); // 输出:"JavaJavaJava"
五、JDK 12(2019):Switch 表达式(预览)
JDK 12 引入更简洁的 Switch 表达式(预览版,JDK 14 正式发布),支持箭头语法和 yield 返回值。
Switch 表达式(JDK 14 正式)
用 -> 替代 :,支持直接返回值,避免 break 冗余。
示例:Switch 表达式
int day = 3;
String dayName;
// 传统 Switch(语句,无返回值)
switch (day) {
case 1:
dayName = "周一";
break;
case 2:
dayName = "周二";
break;
case 3:
dayName = "周三"; // 输出:周三
break;
default:
dayName = "未知";
}
// Switch 表达式(JDK 14+,可直接返回值)
dayName = switch (day) {
case 1 -> "周一";
case 2 -> "周二";
case 3 -> "周三"; // 自动返回 "周三"
default -> "未知";
};
// 复杂逻辑用 yield(需用大括号)
int numLetters = switch (dayName) {
case "周一", "周二", "周三", "周四", "周五" -> 2; // 多个值合并
case "周六", "周日" -> {
System.out.println("周末");
yield 3; // 用 yield 返回值
}
default -> throw new IllegalStateException("无效星期");
};
System.out.println("字母数:" + numLetters); // 输出:2(周三对应2)
六、JDK 16(2021):Record 与 Pattern Matching(正式)
JDK 16 是 LTS 版本,正式发布 Record(不可变数据载体)和改进的 instanceof 模式匹配。
- Record(正式版)
简化不可变数据类的定义(自动生成 equals()、hashCode()、toString() 等方法)。
示例:用 Record 定义数据类
// 传统方式定义不可变类
class TraditionalUser {
private final String name;
private final int age;
public TraditionalUser(String name, int age) {this.name = name;this.age = age;
}// 需手动实现 equals、hashCode、toString...
}
// Record 方式(一行代码搞定)
record RecordUser(String name, int age) {}
public class RecordDemo {
public static void main(String[] args) {
RecordUser user = new RecordUser("张三", 25);
System.out.println(user); // 输出:RecordUser[name=张三, age=25](自动生成 toString)
System.out.println(user.name()); // 输出:张三(自动生成访问器 name())
System.out.println(user.age()); // 输出:25
}
}
- Pattern Matching for instanceof(正式版)
简化 instanceof 类型检查和强制转换的代码。
示例:模式匹配 instanceof
Object obj = "Hello JDK 16";
// 传统方式
if (obj instanceof String) {
String str = (String) obj; // 需显式强制转换
System.out.println(str.length()); // 输出:11
}
// 模式匹配(JDK 16+)
if (obj instanceof String str) { // 自动转换为 String 类型并赋值给 str
System.out.println(str.length()); // 输出:11(无需显式转换)
}
七、JDK 17(2021):密封类与虚拟线程(孵化)
JDK 17 是 LTS 版本,正式发布密封类(Sealed Classes),并孵化虚拟线程(Virtual Threads)。
- 密封类(正式版)
限制类的继承范围(仅允许指定子类继承或实现),增强类型安全性。
示例:密封类与允许的子类
// 密封接口,仅允许 Circle 和 Rectangle 实现
public sealed interface Shape permits Circle, Rectangle {}
// 子类必须声明为 final、sealed 或 non-sealed
final class Circle implements Shape {
private final double radius;
public Circle(double r) { this.radius = r; }
public double area() { return Math.PI * radius * radius; }
}
sealed class Rectangle implements Shape permits Square {} // 子类可进一步限制
final class Square extends Rectangle {
private final double side;
public Square(double s) { this.side = s; }
public double area() { return side * side; }
}
// 模式匹配中可安全使用密封类的子类型
public static double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> c.area(); // 仅允许 Circle
case Rectangle r -> r.area(); // 仅允许 Rectangle(或其允许的子类 Square)
// 无需 default,因为 Shape 的子类已被完全限制
};
}
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape square = new Square(4);
System.out.println(calculateArea(circle)); // 输出:78.5398...
System.out.println(calculateArea(square)); // 输出:16.0
}
- 虚拟线程(孵化,JDK 21 正式)
轻量级线程(用户态线程),大幅降低高并发场景的资源消耗(如百万级连接)。
示例:虚拟线程执行任务
public class VirtualThreadDemo {
public static void main(String[] args) {
// 创建 10 个虚拟线程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("任务 " + taskId + " 运行在:" + Thread.currentThread());
// 模拟耗时操作
Thread.sleep(Duration.ofSeconds(1));
return taskId;
});
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 输出类似:
// 任务 0 运行在:VirtualThread[#1]/runnable@123456
// 任务 1 运行在:VirtualThread[#2]/runnable@7890ab
// ...(每个任务几乎同时启动)
八、JDK 21(2023):虚拟线程正式版与字符串模板
JDK 21 是 LTS 版本,虚拟线程正式发布,并新增字符串模板(String Templates)。
- 虚拟线程(正式版)
如前所述,虚拟线程是 JDK 21 的核心特性之一,适用于高并发 I/O 密集型场景(如 Web 服务器、数据库连接)。
- 字符串模板(正式版)
简化字符串拼接,支持表达式嵌入和安全转义(类似 Python 的 f-string)。
示例:字符串模板
// 定义模板(使用 StringTemplate)
String name = "张三";
int age = 25;
String message = STR."""
用户信息:
姓名:{name}
年龄:{age}
""";
System.out.println(message);
// 输出:
// 用户信息:
// 姓名:张三
// 年龄:25
- 外国函数接口(FFI,正式版)
通过 jdk.incubator.foreign 包安全调用本地代码(如 C/C++),替代 JNI(Java Native Interface)。
示例:调用 C 的 strlen 函数
import jdk.incubator.foreign.;
import static jdk.incubator.foreign.CLinker.;
public class FFIExample {
public static void main(String[] args) {
try (Arena arena = Arena.ofConfined()) {
// 在本地内存中分配字符串(以 null 结尾)
MemorySegment str = arena.allocateUtf8String("Hello FFI");
// 获取 C 的 strlen 函数指针MemorySegment strlenFunc = CLinker.getInstance().lookup("strlen").get();// 调用 strlen(参数:C指针)long length = (long) CLinker.invokeExact(strlenFunc, FunctionDescriptor.of(CLong, CPointer), str.address());System.out.println("字符串长度:" + length); // 输出:10("Hello FFI" 长度为10)}
}
}
总结
JDK 8 后的版本持续聚焦开发效率(Lambda、Stream、Record)、性能优化(向量API、ZGC)、现代化API(日期时间、HTTP Client)和并发模型革新(虚拟线程)。实际项目中,建议优先使用 LTS 版本(如 JDK 8、11、17、21),并根据需求选择特性(如用 Record 简化数据类,用虚拟线程处理高并发)。