Java21新特性教程 - 虚拟线程、模式匹配、Record模式等完整指南
Java21新特性教程 - 全面掌握Java21 LTS版本核心特性
目录
- Java21简介
- 虚拟线程(Virtual Threads)
- 模式匹配增强
- Record模式
- Switch表达式增强
- 字符串模板(预览)
- Sequenced Collections
- 未命名类和实例main方法
- 作用域值(Scoped Values)
- 结构化并发(预览)
- 其他重要特性
- 迁移指南和最佳实践
- 总结与进阶
1. Java21简介
Java 21于2023年9月19日正式发布,是继Java 17之后的又一个长期支持(LTS)版本。Java 21引入了许多革命性的特性,特别是虚拟线程(Virtual Threads)的正式发布,标志着Java并发编程进入新时代。
正式发布特性:
- ✅ 虚拟线程(Virtual Threads):轻量级线程,大幅提升并发性能
- ✅ 模式匹配增强:简化类型检查和转换
- ✅ Record模式:解构Record对象
- ✅ Switch表达式增强:更强大的模式匹配
- ✅ Sequenced Collections:有序集合接口
- ✅ 未命名类和实例main方法:简化Java入门
预览特性:
🔍 字符串模板(String Templates):简化字符串拼接
🔍 结构化并发(Structured Concurrency):简化并发编程
🔍 作用域值(Scoped Values):线程本地变量的替代方案
LTS版本:长期支持,适合生产环境
性能提升:虚拟线程带来巨大性能提升
代码简化:模式匹配等特性简化代码
现代化:跟上Java语言发展趋势
面试必备:Java21特性是面试热点
JDK版本:JDK 21或更高版本
IDE:IntelliJ IDEA 2023.1+、Eclipse 2023-09+等
下载和安装:
# 下载JDK 21
# 访问:https://www.oracle.com/java/technologies/downloads/#java21
# 验证安装
java -version
# 应该显示 java version "21.0.x"
# 设置JAVA_HOME(Windows)
set JAVA_HOME=C:\Program Files\Java\jdk-21
# 设置JAVA_HOME(macOS/Linux)
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-21.jdk/Contents/Home2. 虚拟线程(Virtual Threads)
虚拟线程(Virtual Threads)是Java21引入的最重要的特性之一。它们是轻量级线程,由JVM管理,而不是操作系统线程。一个平台线程(传统线程)可以运行数千个虚拟线程。
| 特性 | 平台线程(Platform Thread) | 虚拟线程(Virtual Thread) |
|---|---|---|
| 创建成本 | 高(~1MB内存) | 低(~几百字节) |
| 数量限制 | 受操作系统限制(~数千) | 可创建数百万个 |
| 适用场景 | CPU密集型任务 | IO密集型任务 |
| 调度方式 | 操作系统调度 | JVM调度 |
方式1:使用Thread.startVirtualThread()
// 创建并启动虚拟线程
Thread virtualThread = Thread.startVirtualThread(() -> {
System.out.println("虚拟线程执行: " + Thread.currentThread());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("虚拟线程完成");
});
// 等待线程完成
virtualThread.join();方式2:使用Thread.ofVirtual()
// 创建虚拟线程构建器
Thread.Builder builder = Thread.ofVirtual().name("my-virtual-thread");
// 创建线程
Thread vt1 = builder.start(() -> {
System.out.println("虚拟线程1");
});
// 创建未启动的线程
Thread vt2 = builder.unstarted(() -> {
System.out.println("虚拟线程2");
});
vt2.start();方式3:使用ExecutorService
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
// 创建虚拟线程执行器
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 提交任务
executor.submit(() -> {
System.out.println("任务1执行");
});
executor.submit(() -> {
System.out.println("任务2执行");
});
// 等待所有任务完成
executor.shutdown();
}示例1:HTTP服务器
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
public class VirtualThreadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
while (true) {
Socket socket = serverSocket.accept();
// 为每个连接创建虚拟线程
executor.submit(() -> {
try {
handleRequest(socket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
private static void handleRequest(Socket socket) throws IOException {
// 处理HTTP请求
// 虚拟线程在IO阻塞时会自动释放平台线程
// 可以轻松处理数万个并发连接
}
}示例2:并发IO操作
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.concurrent.Executors;
public class ConcurrentHttpRequests {
public static void main(String[] args) {
List<String> urls = List.of(
"https://api.example.com/data1",
"https://api.example.com/data2",
"https://api.example.com/data3"
);
HttpClient client = HttpClient.newHttpClient();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
urls.forEach(url -> {
executor.submit(() -> {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);
System.out.println("响应: " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
});
});
}
}
}// ❌ 错误:不要池化虚拟线程
ExecutorService pool = Executors.newFixedThreadPool(10);
// ✅ 正确:直接创建虚拟线程
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 使用executor
}// ✅ 适合:IO操作
virtualThread.submit(() -> {
Files.readString(Path.of("file.txt"));
});
// ❌ 不适合:CPU密集型任务
virtualThread.submit(() -> {
// 大量计算
for (int i = 0; i < 1_000_000_000; i++) {
// 计算
}
});// ❌ 避免:ThreadLocal在虚拟线程中可能有问题
ThreadLocal<String> threadLocal = new ThreadLocal<>();
// ✅ 使用:Scoped Values(Java21新特性)
ScopedValue<String> scopedValue = ScopedValue.newInstance();import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.stream.IntStream;
public class PerformanceComparison {
public static void main(String[] args) {
int taskCount = 10_000;
// 平台线程池(传统方式)
long start1 = System.currentTimeMillis();
try (ExecutorService executor = Executors.newFixedThreadPool(100)) {
IntStream.range(0, taskCount).forEach(i -> {
executor.submit(() -> {
try {
Thread.sleep(100); // 模拟IO
} catch (InterruptedException e) {
e.printStackTrace();
}
});
});
}
long time1 = System.currentTimeMillis() - start1;
// 虚拟线程
long start2 = System.currentTimeMillis();
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, taskCount).forEach(i -> {
executor.submit(() -> {
try {
Thread.sleep(100); // 模拟IO
} catch (InterruptedException e) {
e.printStackTrace();
}
});
});
}
long time2 = System.currentTimeMillis() - start2;
System.out.println("平台线程池耗时: " + time1 + "ms");
System.out.println("虚拟线程耗时: " + time2 + "ms");
}
}3. 模式匹配增强
模式匹配(Pattern Matching)是Java21引入的重要特性,它允许我们使用更简洁的语法进行类型检查和转换。
Java16之前的方式
Object obj = "Hello";
// 传统方式
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.toUpperCase());
}Java16+的方式(模式匹配)
Object obj = "Hello";
// 模式匹配方式
if (obj instanceof String str) {
// str变量自动可用,无需强制转换
System.out.println(str.toUpperCase());
}基本用法
Object obj = "Hello";
// switch模式匹配
String result = switch (obj) {
case String s -> "字符串: " + s;
case Integer i -> "整数: " + i;
case Double d -> "浮点数: " + d;
case null -> "null值";
default -> "未知类型";
};
System.out.println(result);类型模式匹配
Number number = 42;
String result = switch (number) {
case Integer i when i > 0 -> "正整数: " + i;
case Integer i -> "非正整数: " + i;
case Long l -> "长整数: " + l;
case Double d -> "浮点数: " + d;
default -> "其他类型";
};守卫模式(Guarded Patterns)
Object obj = 42;
String result = switch (obj) {
case Integer i when i > 0 && i < 100 -> "小正整数: " + i;
case Integer i when i >= 100 -> "大正整数: " + i;
case Integer i -> "非正整数: " + i;
case String s when s.length() > 5 -> "长字符串: " + s;
case String s -> "短字符串: " + s;
default -> "其他";
};null处理
String str = null;
// Java21之前需要显式处理null
String result = str == null ? "null" : str.toUpperCase();
// Java21模式匹配
String result2 = switch (str) {
case null -> "null";
case String s -> s.toUpperCase();
};示例1:处理不同类型的数据
public class PatternMatchingExample {
public static String process(Object data) {
return switch (data) {
case String s -> "处理字符串: " + s;
case Integer i -> "处理整数: " + (i * 2);
case List<?> list -> "处理列表,大小: " + list.size();
case Map<?, ?> map -> "处理映射,键数量: " + map.size();
case null -> "处理null值";
default -> "未知类型: " + data.getClass().getName();
};
}
public static void main(String[] args) {
System.out.println(process("Hello"));
System.out.println(process(42));
System.out.println(process(List.of(1, 2, 3)));
System.out.println(process(Map.of("key", "value")));
}
}示例2:类型安全的处理
public class TypeSafeProcessor {
public static void processNumber(Number number) {
switch (number) {
case Integer i -> {
System.out.println("整数: " + i);
// i是Integer类型,可以直接使用
int doubled = i * 2;
System.out.println("翻倍: " + doubled);
}
case Double d -> {
System.out.println("浮点数: " + d);
double squared = d * d;
System.out.println("平方: " + squared);
}
case Long l -> {
System.out.println("长整数: " + l);
}
default -> {
System.out.println("其他数字类型");
}
}
}
}4. Record模式
Record是Java14引入的特性,用于创建不可变的数据载体类。Java21增强了Record,支持模式匹配和解构。
// 定义Record
public record Point(int x, int y) {
// 可以添加方法
public double distance() {
return Math.sqrt(x * x + y * y);
}
}
// 使用Record
Point p = new Point(3, 4);
System.out.println(p.x()); // 3
System.out.println(p.y()); // 4
System.out.println(p.distance()); // 5.0基本解构
Point point = new Point(3, 4);
// Record模式匹配
if (point instanceof Point(int x, int y)) {
System.out.println("x = " + x + ", y = " + y);
}switch中的Record模式
sealed interface Shape permits Circle, Rectangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
public class ShapeProcessor {
public static double area(Shape shape) {
return switch (shape) {
case Circle(double radius) -> Math.PI * radius * radius;
case Rectangle(double w, double h) -> w * h;
};
}
public static void main(String[] args) {
Shape circle = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
System.out.println("圆面积: " + area(circle)); // 78.54
System.out.println("矩形面积: " + area(rect)); // 24.0
}
}嵌套Record模式
record Point(int x, int y) {}
record Line(Point start, Point end) {}
public class NestedRecordPattern {
public static void processLine(Line line) {
switch (line) {
case Line(Point(int x1, int y1), Point(int x2, int y2)) -> {
System.out.println("起点: (" + x1 + ", " + y1 + ")");
System.out.println("终点: (" + x2 + ", " + y2 + ")");
}
}
}
}示例1:处理API响应
record ApiResponse<T>(int status, String message, T data) {}
record User(String name, String email) {}
public class ApiResponseProcessor {
public static void processResponse(ApiResponse<?> response) {
switch (response) {
case ApiResponse(int status, String msg, User user)
when status == 200 -> {
System.out.println("成功: " + msg);
System.out.println("用户: " + user.name() + " - " + user.email());
}
case ApiResponse(int status, String msg, Object data)
when status >= 400 -> {
System.out.println("错误 " + status + ": " + msg);
}
default -> {
System.out.println("未知响应");
}
}
}
}示例2:树结构处理
sealed interface TreeNode permits Leaf, Node {}
record Leaf(int value) implements TreeNode {}
record Node(TreeNode left, TreeNode right) implements TreeNode {}
public class TreeProcessor {
public static int sum(TreeNode tree) {
return switch (tree) {
case Leaf(int value) -> value;
case Node(TreeNode left, TreeNode right) ->
sum(left) + sum(right);
};
}
public static void main(String[] args) {
TreeNode tree = new Node(
new Leaf(1),
new Node(
new Leaf(2),
new Leaf(3)
)
);
System.out.println("总和: " + sum(tree)); // 6
}
}5. Switch表达式增强
Java21增强了switch表达式,支持更强大的模式匹配和更简洁的语法。
int day = 3;
// switch表达式(返回值的switch)
String dayName = switch (day) {
case 1 -> "星期一";
case 2 -> "星期二";
case 3 -> "星期三";
case 4 -> "星期四";
case 5 -> "星期五";
case 6, 7 -> "周末";
default -> "未知";
};
System.out.println(dayName); // 星期三int month = 2;
String season = switch (month) {
case 12, 1, 2 -> "冬季";
case 3, 4, 5 -> "春季";
case 6, 7, 8 -> "夏季";
case 9, 10, 11 -> "秋季";
default -> "无效月份";
};int score = 85;
String grade = switch (score / 10) {
case 10, 9 -> {
yield "优秀";
}
case 8 -> {
yield "良好";
}
case 7 -> {
yield "中等";
}
case 6 -> {
yield "及格";
}
default -> {
yield "不及格";
}
};示例1:状态机
enum State {
INIT, PROCESSING, SUCCESS, ERROR
}
public class StateMachine {
public static String processState(State state) {
return switch (state) {
case INIT -> "初始化状态";
case PROCESSING -> "处理中...";
case SUCCESS -> "处理成功";
case ERROR -> "处理失败";
};
}
}示例2:类型转换
public class TypeConverter {
public static String convertToString(Object obj) {
return switch (obj) {
case String s -> s;
case Integer i -> String.valueOf(i);
case Double d -> String.format("%.2f", d);
case Boolean b -> b ? "true" : "false";
case null -> "null";
default -> obj.toString();
};
}
}6. 字符串模板(预览)
字符串模板(String Templates)是Java21的预览特性,提供了更优雅的字符串插值方式。
// 需要启用预览特性
// javac --enable-preview --release 21 Main.java
// java --enable-preview Main
String name = "Java";
int version = 21;
// 字符串模板(预览特性)
String message = STR."Hello \{name} version \{version}";
System.out.println(message); // Hello Java version 21import static java.lang.StringTemplate.STR;
String name = "Alice";
int age = 25;
// 使用STR模板处理器
String greeting = STR."Hello, my name is \{name} and I'm \{age} years old";
System.out.println(greeting);String name = "Bob";
String email = "bob@example.com";
String template = STR."""
Name: \{name}
Email: \{email}
Date: \{java.time.LocalDate.now()}
""";
System.out.println(template);int a = 10;
int b = 20;
String result = STR."\{a} + \{b} = \{a + b}";
System.out.println(result); // 10 + 20 = 307. Sequenced Collections
Java21引入了Sequenced Collections接口,为有序集合提供了统一的API。
import java.util.*;
SequencedCollection<String> list = new ArrayList<>();
list.add("first");
list.add("middle");
list.add("last");
// 获取第一个元素
String first = list.getFirst(); // "first"
// 获取最后一个元素
String last = list.getLast(); // "last"
// 移除第一个元素
String removedFirst = list.removeFirst();
// 移除最后一个元素
String removedLast = list.removeLast();
// 反转顺序
SequencedCollection<String> reversed = list.reversed();SequencedSet<String> set = new LinkedHashSet<>();
set.add("first");
set.add("middle");
set.add("last");
// 获取第一个和最后一个元素
String first = set.getFirst();
String last = set.getLast();
// 反转
SequencedSet<String> reversed = set.reversed();SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.put("first", 1);
map.put("middle", 2);
map.put("last", 3);
// 获取第一个和最后一个条目
Map.Entry<String, Integer> firstEntry = map.firstEntry();
Map.Entry<String, Integer> lastEntry = map.lastEntry();
// 获取第一个和最后一个键
String firstKey = map.firstKey();
String lastKey = map.lastKey();
// 反转
SequencedMap<String, Integer> reversed = map.reversed();public class SequencedCollectionExample {
public static void main(String[] args) {
// 使用SequencedCollection处理有序数据
SequencedCollection<String> history = new ArrayList<>();
// 添加浏览历史
history.add("page1");
history.add("page2");
history.add("page3");
// 获取最近访问的页面
String recent = history.getLast();
System.out.println("最近访问: " + recent);
// 获取最早访问的页面
String oldest = history.getFirst();
System.out.println("最早访问: " + oldest);
// 反向遍历
for (String page : history.reversed()) {
System.out.println(page);
}
}
}8. 未命名类和实例main方法
Java21允许编写没有显式类声明的程序,简化了Java入门。
// 不需要public class声明
// 直接编写代码
void main() {
System.out.println("Hello, Java 21!");
}
// 或者使用String[]参数
void main(String[] args) {
System.out.println("参数数量: " + args.length);
for (String arg : args) {
System.out.println("参数: " + arg);
}
}// 可以直接编写代码,无需类声明
import java.util.*;
void main() {
List<String> names = List.of("Alice", "Bob", "Charlie");
names.forEach(name ->
System.out.println("Hello, " + name)
);
int sum = calculateSum(10, 20);
System.out.println("总和: " + sum);
}
int calculateSum(int a, int b) {
return a + b;
}// 简单的脚本式编程
import java.time.LocalDate;
void main() {
String name = "Java";
int version = 21;
LocalDate releaseDate = LocalDate.of(2023, 9, 19);
System.out.println(STR."""
Language: \{name}
Version: \{version}
Release Date: \{releaseDate}
""");
}9. 作用域值(Scoped Values)
作用域值(Scoped Values)是Java21引入的特性,用于替代ThreadLocal,特别适合虚拟线程。
import java.util.concurrent.StructuredTaskScope;
// 定义作用域值
final ScopedValue<String> USER_NAME = ScopedValue.newInstance();
public class ScopedValueExample {
public static void main(String[] args) {
// 绑定值到作用域
ScopedValue.runWhere(USER_NAME, "Alice", () -> {
processUser();
});
}
static void processUser() {
// 读取作用域值
String userName = USER_NAME.get();
System.out.println("处理用户: " + userName);
// 调用其他方法,值会自动传递
processUserData();
}
static void processUserData() {
String userName = USER_NAME.get();
System.out.println("处理用户数据: " + userName);
}
}final ScopedValue<String> CONTEXT = ScopedValue.newInstance();
public class VirtualThreadScopedValue {
public static void main(String[] args) {
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
// 为每个虚拟线程设置作用域值
ScopedValue.runWhere(CONTEXT, "Task1", () -> {
executor.submit(() -> {
System.out.println("任务1上下文: " + CONTEXT.get());
});
});
ScopedValue.runWhere(CONTEXT, "Task2", () -> {
executor.submit(() -> {
System.out.println("任务2上下文: " + CONTEXT.get());
});
});
}
}
}10. 结构化并发(预览)
结构化并发(Structured Concurrency)是Java21的预览特性,提供了更好的并发编程模型。
import java.util.concurrent.StructuredTaskScope;
import java.util.concurrent.Future;
public class StructuredConcurrencyExample {
public static void main(String[] args) throws Exception {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 提交多个任务
Future<String> task1 = scope.fork(() -> fetchData1());
Future<String> task2 = scope.fork(() -> fetchData2());
Future<String> task3 = scope.fork(() -> fetchData3());
// 等待所有任务完成或失败
scope.join();
scope.throwIfFailed();
// 获取结果
String result1 = task1.resultNow();
String result2 = task2.resultNow();
String result3 = task3.resultNow();
System.out.println("结果: " + result1 + ", " + result2 + ", " + result3);
}
}
static String fetchData1() throws InterruptedException {
Thread.sleep(100);
return "Data1";
}
static String fetchData2() throws InterruptedException {
Thread.sleep(150);
return "Data2";
}
static String fetchData3() throws InterruptedException {
Thread.sleep(200);
return "Data3";
}
}try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> task1 = scope.fork(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("任务失败");
}
return "成功";
});
scope.join();
scope.throwIfFailed(); // 如果有任务失败,抛出异常
String result = task1.resultNow();
} catch (Exception e) {
System.err.println("任务执行失败: " + e.getMessage());
}11. 其他重要特性
public record Pair<T, U>(T first, U second) {
public Pair<T, U> swap() {
return new Pair<>(second, first);
}
}
// 使用
Pair<String, Integer> pair = new Pair<>("Hello", 42);
Pair<Integer, String> swapped = pair.swap();Java21包含了对ZGC和G1GC的改进,提供了更好的性能。
- 启动时间优化
- 内存使用优化
- JIT编译器改进
12. 迁移指南和最佳实践
<!-- pom.xml -->
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties># 编译时
javac --enable-preview --release 21 Main.java
# 运行时
java --enable-preview Main- 先使用虚拟线程处理IO密集型任务
- 使用模式匹配简化类型检查
- 使用Record模式处理数据类
// ✅ 适合:IO密集型任务
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// HTTP请求、数据库查询、文件IO等
});
}
// ❌ 不适合:CPU密集型任务
// 使用平台线程池处理CPU密集型任务// ✅ 使用模式匹配简化代码
String result = switch (obj) {
case String s -> s.toUpperCase();
case Integer i -> String.valueOf(i);
default -> "unknown";
};
// ❌ 避免过度使用,保持代码可读性// ✅ 使用Record表示不可变数据
public record User(String name, String email) {}
// ❌ 不要用Record替代所有类
// Record适合简单的数据载体Q1: 虚拟线程会替代平台线程吗?
A: 不会。虚拟线程适合IO密集型任务,平台线程仍然适合CPU密集型任务。
Q2: 什么时候使用模式匹配?
A: 当需要根据类型进行不同处理时,模式匹配可以简化代码。
Q3: Record可以继承吗?
A: Record是final的,不能继承。但可以实现接口。
13. 总结与进阶
- ✅ 虚拟线程:革命性的并发模型,大幅提升IO性能
- ✅ 模式匹配:简化类型检查和转换
- ✅ Record模式:优雅的数据处理
- ✅ Switch增强:更强大的控制流
- ✅ 字符串模板:更优雅的字符串处理
- ✅ Sequenced Collections:统一的有序集合API
- 基础掌握:理解虚拟线程、模式匹配、Record模式
- 实践应用:在实际项目中使用新特性
- 性能优化:利用虚拟线程优化IO性能
- 深入理解:学习JVM实现原理
- 官方文档:https://docs.oracle.com/en/java/javase/21/
- JEP列表:查看Java21的所有JEP
- 社区资源:关注Java社区的最新动态
- 深入虚拟线程:理解虚拟线程的实现原理
- 性能测试:对比虚拟线程和平台线程的性能
- 最佳实践:总结项目中的最佳实践
- 关注新版本:关注Java后续版本的新特性
结语
Java21作为最新的LTS版本,引入了许多革命性的特性,特别是虚拟线程的正式发布,标志着Java并发编程的新时代。通过本教程的学习,相信你已经掌握了Java21的核心特性。
记住:
- 多实践:理论结合实践,多写代码
- 理解原理:深入理解新特性的实现原理
- 关注性能:利用新特性优化应用性能
- 持续学习:关注Java语言的发展
祝你学习愉快,编程顺利! 🚀
本教程由Java突击队学习社区编写,如有问题欢迎反馈。