第一章 Java 版本更替
1.1 发布节奏变革
| 时期 | 发布策略 | 代表版本 |
|---|---|---|
| 2011 年之前 | 不定期大版本 | Java 5/6/7 |
| 2017 年 9 月起 | 每 6 个月固定发布 | Java 9 → Java 25+ |
| LTS 策略 | 每 2~3 年指定一个 LTS | 7, 8, 11, 17, 21 |
核心理念 :自 Java 9 起,Oracle 采用"列车发布模型"(Time-Train Release),每 6 个月交付一个版本。特性先以 Preview(预览) 状态引入,经社区反馈迭代后转为 正式(GA) 状态。
1.2 演进主线
Java 7(语法糖)
↓
Java 8(函数式编程:Lambda + Stream)
↓
Java 9-11(模块化 + 类型推断 + HTTP/2)
↓
Java 12-15(Switch 表达式 + 文本块 + Records 预览)
↓
Java 16-17(Records/密封类/模式匹配 instanceof 正式版)
↓
Java 18-20(Scoped Values + 虚拟线程预览孵化)
↓
Java 21(虚拟线程 + Switch 模式匹配 + Record Patterns + 顺序集合)
第二章 Java 7 --- 语法糖与开发体验优化
发布日期 :2011年7月 | 状态 : LTS | 代号:Project Coin
2.1 try-with-resources(自动资源管理)
背景 :Java 6 及以前版本中,开发者必须在 finally 块中手动关闭资源(如流、连接),代码冗长且易出错。
语法规则:
java
// 资源声明在 try() 中,需实现 AutoCloseable 接口
try (ResourceType resource = new ResourceType()) {
// 使用资源
} catch (ExceptionType e) {
// 处理异常
}
// 资源自动关闭,无需 finally
对比:
java
// ===== Java 6 及之前 =====
FileInputStream fis = null;
try {
fis = new FileInputStream("file.txt");
// 读取文件...
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try { fis.close(); } catch (IOException e) { /* 忽略 */ }
}
}
// ===== Java 7 =====
try (FileInputStream fis = new FileInputStream("file.txt")) {
// 读取文件...
} catch (IOException e) {
e.printStackTrace();
}
应用场景:所有涉及 I/O 流、数据库连接、网络连接等需要显式关闭的资源。
2.2 多异常捕获(Multi-Catch)
语法规则:
java
// 使用 | 分隔多种异常类型
try {
// 可能抛出 IOException 或 SQLException 的代码
} catch (IOException | SQLException e) {
// 统一处理,e 的类型为两种异常的最近公共父类
logger.error("操作失败", e);
}
与旧版对比 :Java 6 中需为每种异常编写单独的 catch 块,导致大量重复代码。
2.3 菱形运算符(Diamond Operator)
java
// Java 6:冗余的类型声明
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
// Java 7:编译器自动推断泛型类型
Map<String, List<Integer>> map = new HashMap<>();
2.4 switch 支持 String
java
String command = "start";
switch (command) { // Java 7 之前只支持 byte/short/int/char/enum
case "start":
System.out.println("启动服务");
break;
case "stop":
System.out.println("停止服务");
break;
default:
System.out.println("未知命令");
}
2.5 数字字面量增强
java
int binary = 0b1010_0001; // 二进制字面量 + 下划线分隔
long creditCard = 1234_5678_9012_3456L; // 提升可读性
int hex = 0xFF_EC_DE_5E; // 十六进制也可使用
第三章 Java 8 --- 函数式编程
发布日期 :2014年3月 | 状态: LTS(最具里程碑意义的版本)
3.1 Lambda 表达式
背景:Java 一直缺乏将行为(函数)作为参数传递的能力。匿名内部类虽可实现类似效果,但语法冗长。Lambda 表达式的引入使 Java 具备了函数式编程的核心能力。
语法规则:
java
// 基本形式
(parameters) -> expression
(parameters) -> { statements; }
// 类型可省略(编译器推断)
// 单参数可省略括号
// 单表达式可省略大括号和 return
实际示例:
java
// ===== Java 7:匿名内部类 =====
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
// ===== Java 8:Lambda =====
Collections.sort(list, (a, b) -> a.length() - b.length());
// 进一步简化:方法引用
list.sort(Comparator.comparingInt(String::length));
核心约束:
- Lambda 只能赋值给函数式接口(仅含一个抽象方法的接口)
- Lambda 中
this指向外部类实例(不同于匿名内部类) - 变量捕获遵循 effectively final 规则
3.2 函数式接口(Functional Interfaces)
java
@FunctionalInterface // 编译期校验
public interface Predicate<T> {
boolean test(T t);
}
内置核心函数式接口 (java.util.function 包):
| 接口 | 方法签名 | 用途 |
|---|---|---|
Predicate<T> |
boolean test(T t) |
条件判断 |
Function<T,R> |
R apply(T t) |
数据转换 |
Consumer<T> |
void accept(T t) |
消费处理 |
Supplier<T> |
T get() |
数据供给 |
BiFunction<T,U,R> |
R apply(T t, U u) |
双参数转换 |
UnaryOperator<T> |
T apply(T t) |
一元运算 |
3.3 Stream API
背景:集合操作的痛点在于:外部迭代导致代码冗长、难以并行化、无法进行链式操作。Stream API 引入了声明式的数据处理管道。
语法规则:
java
// 流水线模型:Source → Intermediate Operations → Terminal Operation
stream()
.filter(...) // 过滤(中间操作,惰性求值)
.map(...) // 映射(中间操作,惰性求值)
.sorted(...) // 排序(中间操作,有状态)
.collect(...) // 收集(终端操作,触发执行)
实际示例:
java
// 需求:找出所有价格 > 100 的商品名称,按价格降序排列
List<String> expensiveNames = products.stream()
.filter(p -> p.getPrice() > 100)
.sorted(Comparator.comparingDouble(Product::getPrice).reversed())
.map(Product::getName)
.collect(Collectors.toList());
// 并行流(利用多核 CPU)
long count = hugeList.parallelStream()
.filter(x -> x > 0)
.count();
与旧版对比:
java
// Java 7:命令式
List<String> result = new ArrayList<>();
for (Product p : products) {
if (p.getPrice() > 100) {
result.add(p.getName());
}
}
Collections.sort(result, /* ... */);
3.4 Optional
背景 :NullPointerException 是 Java 中最常见的运行时异常。Optional 提供了一种显式表达"值可能不存在"的容器类型。
java
// 创建
Optional<String> opt = Optional.of("hello"); // 值不能为 null
Optional<String> opt = Optional.ofNullable(null); // 值可为 null
Optional<String> empty = Optional.empty();
// 使用
String name = optional.orElse("默认名称");
String name = optional.orElseGet(() -> computeDefault());
optional.ifPresent(n -> System.out.println(n));
String upper = optional.map(String::toUpperCase).orElse("");
最佳实践 :Optional 应用于方法返回值,不应用于字段或方法参数。
3.5 接口默认方法与静态方法
java
public interface Collection<E> {
// 默认方法:不破坏已有实现
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
// 静态方法
static <T> Collection<T> empty() {
return Collections.emptyList();
}
}
设计意义 :允许在不破坏向后兼容性的情况下向接口添加新方法(Java 8 为已有集合接口添加 stream() 方法正是依赖此特性)。
3.6 新日期时间 API(java.time)
java
// 不可变、线程安全的日期时间类
LocalDate today = LocalDate.now(); // 2026-06-28
LocalTime now = LocalTime.of(14, 30, 15);
LocalDateTime dateTime = LocalDateTime.now();
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 日期运算
LocalDate nextWeek = today.plusWeeks(1);
Period period = Period.between(startDate, endDate);
Duration duration = Duration.between(startTime, endTime);
// 格式化
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = dateTime.format(fmt);
与旧版对比 :java.util.Date 和 Calendar 可变、非线程安全、API 设计不合理。
3.7 方法引用(Method References)
java
// 四种形式
Function<String, Integer> f1 = Integer::parseInt; // 静态方法引用
Consumer<String> f2 = System.out::println; // 实例方法引用(特定对象)
Function<String, String> f3 = String::toUpperCase; // 实例方法引用(任意对象)
Supplier<ArrayList<String>> f4 = ArrayList::new; // 构造方法引用
3.8 CompletableFuture
java
// 异步编程,支持链式组合
CompletableFuture.supplyAsync(() -> fetchUser(userId))
.thenApply(user -> user.getEmail())
.thenCompose(email -> sendNotificationAsync(email))
.exceptionally(ex -> { log.error("失败", ex); return null; })
.join();
第四章 Java 9 --- 模块化时代
发布日期 :2017年9月 | 状态:非 LTS
4.1 模块化系统(Project Jigsaw / JPMS)
背景 :随着 JDK 体积不断膨胀(rt.jar 超过 60MB),JVM 启动时需加载整个运行时库。同时,public 类的可见性无法在包级别之上进行有效控制,导致 API 边界模糊。
语法规则:
java
// module-info.java
module com.example.app {
requires java.base; // 隐式依赖,可省略
requires java.sql; // 普通依赖
requires transitive java.logging; // 传递依赖
exports com.example.api; // 导出公共 API
exports com.example.util to com.example.test; // 限定导出
opens com.example.model to com.google.gson; // 反射访问(运行时)
uses com.example.spi.Plugin; // 服务消费者
provides com.example.spi.Plugin with com.example.impl.MyPlugin; // 服务提供者
}
核心概念:
| 关键字 | 含义 |
|---|---|
requires |
声明模块依赖 |
exports |
声明哪些包对外可见 |
opens |
允许反射访问(运行时) |
uses / provides |
服务发现机制 |
与旧版对比 :Java 8 中所有 public 类对 classpath 上的所有代码可见,无法实现真正的封装。
4.2 JShell --- 交互式编程工具
bash
$ jshell
jshell> int x = 10;
x ==> 10
jshell> System.out.println(x * 2);
20
jshell> /list # 查看历史代码
jshell> /save session.java # 保存会话
应用场景:快速验证代码片段、教学演示、调试探索。
4.3 集合工厂方法
java
// Java 8:创建不可变集合非常冗长
List<String> list = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
// Java 9:简洁的工厂方法
List<String> list = List.of("a", "b", "c");
Set<String> set = Set.of("a", "b", "c");
Map<String, Integer> map = Map.of("a", 1, "b", 2);
Map<String, Integer> map2 = Map.ofEntries(
Map.entry("a", 1),
Map.entry("b", 2)
);
注意 :List.of() 返回的是不可变 集合,不支持 add()/remove() 操作。
4.4 接口私有方法
java
public interface Logger {
default void logInfo(String msg) { log("INFO", msg); }
default void logError(String msg) { log("ERROR", msg); }
// 私有方法:复用默认方法中的逻辑
private void log(String level, String msg) {
System.out.println("[" + level + "] " + msg);
}
}
4.5 改进的 Stream API
java
// takeWhile:取满足条件的前缀元素
Stream.of(1, 2, 3, 4, 1, 2).takeWhile(n -> n < 4); // [1, 2, 3]
// dropWhile:丢弃满足条件的前缀元素
Stream.of(1, 2, 3, 4, 1, 2).dropWhile(n -> n < 4); // [4, 1, 2]
// iterate 增加终止条件(类似 for 循环)
Stream.iterate(0, n -> n < 100, n -> n + 2) // 0, 2, 4, ..., 98
.forEach(System.out::println);
4.6 响应式流(Reactive Streams)
java
// java.util.concurrent.Flow(发布-订阅模型)
// 为后续的响应式编程框架(如 RxJava、Project Reactor)提供了标准接口
Flow.Publisher<Item> publisher = ...;
Flow.Subscriber<Item> subscriber = ...;
publisher.subscribe(subscriber);
第五章 Java 10 --- 类型推断
发布日期 :2018年3月 | 状态:非 LTS
5.1 局部变量类型推断(var)
背景:Java 的类型系统虽然保证了安全性,但在局部变量声明时存在大量冗余的类型书写。
语法规则:
java
// var 只能用于局部变量声明,不能用于字段、方法参数、返回类型
var list = new ArrayList<String>(); // 推断为 ArrayList<String>
var map = new HashMap<String, List<Integer>>();
var stream = list.stream(); // 推断为 Stream<String>
// 以下用法不合法
var x; // 错误:必须初始化
var x = null; // 错误:无法推断类型
var arr = {1, 2, 3}; // 错误:数组字面量不支持
// Java 10 新增:var 可用于 for 循环和 try-with-resources
for (var entry : map.entrySet()) { ... }
try (var reader = new BufferedReader(new FileReader("file.txt"))) { ... }
与旧版对比:
java
// Java 9
Map<String, List<CompletableFuture<String>>> futures = new HashMap<>();
// Java 10
var futures = new HashMap<String, List<CompletableFuture<String>>>();
最佳实践 :仅在右侧初始化表达式能清晰表达类型时使用 var,避免降低可读性。
5.2 应用类数据共享(AppCDS)
允许在多个 JVM 实例之间共享类元数据,减少启动时间和内存占用。
5.3 copyOf() 与 toUnmodifiableList()
java
var mutableList = new ArrayList<>(List.of("a", "b"));
var immutableCopy = List.copyOf(mutableList); // 返回不可变副本
// Collector 直接收集为不可变集合
var result = stream.collect(Collectors.toUnmodifiableList());
第六章 Java 11 --- HTTP 客户端与字符串增强
发布日期 :2018年9月 | 状态: LTS
6.1 新 HTTP 客户端(正式版)
背景 :HttpURLConnection 是 Java 1.1 时代的产物,API 陈旧、不支持 HTTP/2 和异步调用。
java
HttpClient client = HttpClient.newHttpClient();
// 同步请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"name\":\"Alice\"}"))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
// 异步请求
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
6.2 String 新方法
java
" hello ".strip(); // "hello"(支持 Unicode 空白)
" hello ".stripLeading(); // "hello "
" hello ".stripTrailing();// " hello"
"".isBlank(); // true(空字符串或仅含空白)
"line1\nline2\nline3".lines(); // Stream<String>: ["line1", "line2", "line3"]
"ha".repeat(3); // "hahaha"
6.3 Lambda 中使用 var
java
// Java 11 允许在 Lambda 参数上使用 var(主要用于添加注解)
(@NotNull var x, @NotNull var y) -> x + y
// 必须所有参数都使用 var 或都不使用
6.4 单文件源代码运行
bash
# 无需编译,直接运行
$ java HelloWorld.java
6.5 其他重要变更
| 特性 | 说明 |
|---|---|
| ZGC(实验性) | 低延迟垃圾回收器 |
| Flight Recorder | 开源(原为 Oracle JDK 商业特性) |
| 移除 Java EE 模块 | java.xml.ws、java.xml.bind 等被移除 |
| 移除 Nashorn | JavaScript 引擎被标记为弃用 |
第七章 Java 12~13 --- Switch 表达式与文本块预览
7.1 Switch 表达式(Java 12 预览 / Java 13 第二预览 / Java 14 正式版)
背景 :传统 switch 语句存在诸多缺陷:fall-through 语义容易出错、不能作为表达式返回值、不支持模式匹配。
java
// ===== 传统 switch 语句 =====
String result;
switch (day) {
case MONDAY:
case FRIDAY:
result = "工作日";
break;
case SUNDAY:
result = "休息日";
break;
default:
result = "其他";
}
// ===== Java 14+ Switch 表达式 =====
String result = switch (day) {
case MONDAY, FRIDAY -> "工作日";
case SUNDAY -> "休息日";
default -> "其他";
};
// 多语句块使用 yield
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
default -> {
int len = day.toString().length();
yield len; // yield 返回值(区别于 return)
}
};
7.2 文本块(Java 13 预览 / Java 14 第二预览 / Java 15 正式版)
背景 :Java 中编写多行字符串(如 SQL、JSON、HTML)需要使用大量 + 拼接和 \n 转义,可读性极差。
java
// ===== Java 12 及之前 =====
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30\n" +
"}";
// ===== Java 15+ 文本块 =====
String json = """
{
"name": "Alice",
"age": 30
}
""";
// 支持转义序列
String code = """
public class Hello {
public static void main(String[] args) {
System.out.println("Hello!"); \s
}
}
""";
// \s 保留尾部空格,\ 续行符避免换行
第八章 Java 14~15 --- Records 预览与 NullPointerException 增强
8.1 Records(Java 14 预览 / Java 15 第二预览 / Java 16 正式版)
背景 :Java 中定义纯数据载体类(DTO、VO)需要编写大量样板代码(字段、构造器、getter、equals、hashCode、toString)。
语法规则:
java
// 一行定义完整的数据载体类
public record Point(int x, int y) {}
// 等价于(自动生成):
// - private final int x, y
// - 全参构造器
// - 访问方法 x(), y()(注意:不是 getX())
// - equals(), hashCode(), toString()
// 使用
var p = new Point(1, 2);
System.out.println(p.x()); // 1
System.out.println(p); // Point[x=1, y=2]
高级特性:
java
public record User(String name, int age) {
// 紧凑构造器(校验逻辑)
public User {
if (age < 0) throw new IllegalArgumentException("年龄不能为负数");
name = name.strip(); // 规范化
}
// 自定义方法
public boolean isAdult() {
return age >= 18;
}
// 可实现接口
// 可定义静态字段和方法
// 不能继承其他类(隐式继承 java.lang.Record)
// 字段天然不可变(final)
}
与 Lombok @Data 对比:
| 特性 | Record | Lombok @Data |
|---|---|---|
| 标准化 | 语言级特性 | 第三方注解处理器 |
| 可变性 | 不可变 | 可变 |
| 继承 | 不可继承 | 可继承 |
| getter 命名 | name() |
getName() |
8.2 NullPointerException 增强诊断(Java 14)
java
// Java 13 及之前
Exception: java.lang.NullPointerException
// Java 14+(启用 -XX:+ShowCodeDetailsInExceptionMessages)
Exception: Cannot invoke "String.length()" because "a.b.name" is null
// 精确定位到调用链中哪个环节为 null
8.3 instanceof 模式匹配(Java 14 预览 / Java 15 第二预览 / Java 16 正式版)
java
// ===== Java 15 及之前 =====
if (obj instanceof String) {
String s = (String) obj; // 冗余的类型转换
System.out.println(s.length());
}
// ===== Java 16+ =====
if (obj instanceof String s) { // 类型检查 + 变量绑定一步完成
System.out.println(s.length());
}
// 可在条件中组合使用
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
}
8.4 Switch 表达式(Java 14 正式版)
参见7.1节,在 Java 14 中正式成为标准特性。
第九章 Java 16~17 --- Records 正式版、密封类与模式匹配
Java 16 :2021年3月 | Java 17:2021年9月 | LTS
9.1 Records 正式版(Java 16)
参见8.1节,在 Java 16 正式成为标准特性。
9.2 instanceof 模式匹配正式版(Java 16)
参见8.3节。
9.3 密封类(Sealed Classes)(Java 15 预览 / Java 16 第二预览 / Java 17 正式版)
背景 :Java 中的继承模型只有两个极端选择:final(完全禁止继承)或完全开放(任何人都可以继承)。缺乏中间地带来控制"谁可以继承我"。
语法规则:
java
// sealed 声明密封类/接口
// permits 列出所有允许的子类(必须在同一模块/包中)
public sealed class Shape
permits Circle, Rectangle, Triangle {
}
// 子类必须选择:final / sealed / non-sealed 之一
public final class Circle extends Shape {
private final double radius;
}
public non-sealed class Rectangle extends Shape {
// non-sealed:重新开放继承
private final double width, height;
}
public sealed class Triangle extends Shape
permits IsoscelesTriangle {
// 继续密封
}
三种子类修饰符:
| 修饰符 | 含义 |
|---|---|
final |
终止继承链 |
sealed |
继续控制继承(需声明 permits) |
non-sealed |
重新开放,任何人都可继承 |
与模式匹配的组合(Java 21+):
java
// 密封类 + switch 模式匹配 = 穷举检查
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> /* ... */ 0;
// 编译器知道所有子类,无需 default!
};
应用场景:
- 领域模型中有限状态/类型的精确建模
- 与模式匹配配合实现类似 ADT(代数数据类型)的效果
- 替代传统的 Visitor 模式
9.4 文本块正式版(Java 15)
参见7.2节。
9.5 其他 Java 17 重要变更
| 特性 | 说明 |
|---|---|
| 强封装 JDK 内部 API | --illegal-access=deny 成为默认值 |
| 新 macOS 渲染管道 | 基于 Metal 的管道 |
| 弃用移除 | 移除实验性 AOT 和 JIT 编译器 |
| 增强的伪随机数生成器 | 新增 RandomGenerator 接口和多种算法 |
第十章 Java 18~20 --- 简单 Web 服务器、Scoped Values 与预览特性孵化
10.1 Java 18(2022年3月,非 LTS)
简单 Web 服务器
bash
# 命令行启动静态文件服务器
$ jwebserver -p 8080 -d /path/to/site
# 输出: Serving /path/to/site on http://127.0.0.1:8080
UTF-8 作为默认字符集
- 整个 JDK 的默认字符集从平台相关改为 UTF-8
- 消除了跨平台编码不一致的问题
10.2 Java 19(2022年9月,非 LTS)
虚拟线程(Preview)
首次引入虚拟线程预览版(详见 Java 21 章节)。
结构化并发(Incubator)
java
// 将多个并发子任务绑定为一个单元,统一生命周期管理
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<String> user = scope.fork(() -> fetchUser());
Future<Integer> order = scope.fork(() -> fetchOrder());
scope.join();
scope.throwIfFailed();
return new Response(user.resultNow(), order.resultNow());
}
Record Patterns(Preview)
java
// 在 instanceof 中解构 Record
if (obj instanceof Point(int x, int y)) {
System.out.println(x + y);
}
10.3 Java 20(2023年3月,非 LTS)
Scoped Values(Incubator)
java
// 替代 ThreadLocal 的更安全方案
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
ScopedValue.runWhere(CURRENT_USER, user, () -> {
processRequest(); // 在此范围内可通过 CURRENT_USER.get() 获取值
});
unnamed 模式与变量(Preview)
java
// 使用 _ 忽略不需要的变量
switch (obj) {
case Integer _ -> System.out.println("是个整数");
case String _ -> System.out.println("是个字符串");
}
第十一章 Java 21 --- 虚拟线程、模式匹配 Switch 与 Record 模式
发布日期 :2023年9月 | 状态: LTS(自 Java 17 以来最重大的 LTS 版本)
11.1 虚拟线程(Virtual Threads)--- JEP 444
背景:传统 Java 线程(平台线程)与操作系统线程 1:1 映射,每个线程默认占用约 1MB 栈内存。在高并发 I/O 密集型场景下(如 Web 服务器处理数万并发连接),创建数千个线程即消耗数 GB 内存,且上下文切换成本高昂。
这是 Project Loom 的核心成果,被誉为"高并发游戏规则改变者"。
语法规则:
java
// 方式一:直接创建虚拟线程
Thread vt = Thread.ofVirtual()
.name("vt-", 0) // 线程名前缀 + 序号
.unstarted(() -> {
System.out.println("Hello from virtual thread!");
System.out.println("Thread: " + Thread.currentThread());
});
vt.start();
// 方式二:虚拟线程执行器(推荐)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 100_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1)); // 阻塞不会浪费 OS 线程
return i;
});
});
}
// 方式三:工厂模式
ThreadFactory factory = Thread.ofVirtual().factory();
Thread vt = factory.newThread(() -> doWork());
与平台线程对比:
| 特性 | 平台线程 | 虚拟线程 |
|---|---|---|
| 映射 | 1:1(OS 线程) | M:N(JVM 调度) |
| 内存占用 | ~1MB/线程 | ~几KB/线程 |
| 可创建数量 | 数千 | 数百万 |
| 阻塞代价 | 高(占用 OS 线程) | 低(自动让出载体线程) |
| 适用场景 | CPU 密集型 | I/O 密集型 |
| synchronized 支持 | ✅ | ✅(但会 pin 载体线程) |
最佳实践:
- 使用
ReentrantLock替代synchronized(避免 pinning) - 不要池化虚拟线程(用完即弃)
- 避免长时间 CPU 密集计算(会占用载体线程)
11.2 Switch 模式匹配(Pattern Matching for switch)--- JEP 441
背景 :instanceof 模式匹配只解决了类型检查的问题,但当需要对多种类型分别处理时,仍需编写大量 if-else if 链。
java
// ===== Java 17 及之前 =====
static String format(Object obj) {
if (obj instanceof Integer i) {
return "整数: " + i;
} else if (obj instanceof String s) {
return "字符串: " + s;
} else if (obj instanceof Long l) {
return "长整数: " + l;
} else {
return "未知类型";
}
}
// ===== Java 21 =====
static String format(Object obj) {
return switch (obj) {
case Integer i -> "整数: " + i;
case String s -> "字符串: " + s;
case Long l -> "长整数: " + l;
case null -> "null值"; // 支持 null 分支!
default -> "未知类型: " + obj;
};
}
Guarded Patterns(守卫模式):
java
// 在 case 中添加额外条件
String result = switch (obj) {
case Integer i when i > 0 -> "正整数: " + i;
case Integer i when i == 0 -> "零";
case Integer i -> "负整数: " + i;
case String s when s.isBlank() -> "空白字符串";
case String s -> "字符串: " + s;
default -> "其他";
};
与密封类的完美配合:
java
sealed interface Expr permits Const, Add, Mul {}
record Const(int value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}
record Mul(Expr left, Expr right) implements Expr {}
// 编译器可以进行穷举检查,无需 default
int eval(Expr expr) {
return switch (expr) {
case Const c -> c.value();
case Add a -> eval(a.left()) + eval(a.right());
case Mul m -> eval(m.left()) * eval(m.right());
// 密封类保证了所有子类已列出,编译器不需要 default
};
}
11.3 Record Patterns(记录模式)--- JEP 440
背景 :Record 提供了数据封装,但提取数据仍需调用访问方法。Record Patterns 允许在模式匹配中解构 Record,直接访问其组件。
java
record Point(int x, int y) {}
record Line(Point start, Point end) {}
// ===== 不使用 Record Patterns =====
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x + y);
}
// ===== 使用 Record Patterns =====
// 一层解构
if (obj instanceof Point(int x, int y)) {
System.out.println(x + y);
}
// 嵌套解构
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
double length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
System.out.println("线段长度: " + length);
}
// 在 switch 中使用
double totalLength = switch (shape) {
case Circle(var r) -> 2 * Math.PI * r;
case Rectangle(var w, var h) -> 2 * (w + h);
case Line(Point(var x1, var y1), Point(var x2, var y2)) ->
Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2));
};
// 在 for-each 中使用
List<Point> points = List.of(new Point(1, 2), new Point(3, 4));
for (Point(var x, var y) : points) {
System.out.printf("(%d, %d)%n", x, y);
}
11.4 顺序集合(Sequenced Collections)--- JEP 431
背景 :Java 集合框架中,List、LinkedHashSet、SortedSet 等都有"有序"的概念,但缺乏统一的接口来访问首/尾元素或反转顺序。
java
// 新增三个接口
// SequencedCollection<E> ← 定义有序集合的通用操作
// SequencedSet<E> ← 有序集合(去重)
// SequencedMap<K,V> ← 有序映射
// 统一的首尾元素访问
SequencedCollection<String> list = new ArrayList<>(List.of("a", "b", "c"));
list.getFirst(); // "a"
list.getLast(); // "c"
list.addFirst("z"); // ["z", "a", "b", "c"]
list.addLast("d"); // ["z", "a", "b", "c", "d"]
list.reversed(); // ["d", "c", "b", "a", "z"](反转视图)
// SequencedMap
SequencedMap<String, Integer> map = new LinkedHashMap<>();
map.putFirst("a", 1);
map.putLast("z", 26);
map.firstEntry(); // a=1
map.lastEntry(); // z=26
map.reversed(); // 反转视图
与旧版对比:
java
// Java 20 及之前:不同集合访问首尾元素的方式不同
list.get(0); // List
list.get(list.size() - 1); // List
set.first(); // SortedSet
set.last(); // SortedSet
deque.getFirst(); // Deque
// 没有统一接口!
11.5 分代 ZGC(Generational ZGC)
bash
# 启用分代 ZGC
-XX:+UseZGC -XX:+ZGenerational
将堆分为年轻代和老年代,显著降低 GC 停顿时间(亚毫秒级),提升吞吐量。
11.6 Unnamed Classes 与 Instance Main Methods(Preview)
java
// 无需类声明、无需 static、无需 String[] args
void main() {
System.out.println("Hello, World!");
}
设计目标:降低 Java 入门门槛,让初学者从第一行代码就能运行程序。
11.7 String Templates(Preview --- JEP 430)
java
// 字符串模板(类似 Kotlin/JavaScript 的模板字符串)
String name = "Alice";
int age = 30;
String msg = STR."Hello, \{name}! You are \{age} years old.";
// 支持表达式
String info = STR."2 + 3 = \{2 + 3}";
// 多行
String json = STR."""
{
"name": "\{name}",
"age": \{age}
}
""";
注意:String Templates 在 Java 21 中为 Preview 状态,后续版本中经历了重大重新设计,Java 23 中被移除预览,预计将以全新方案重新引入。
附录:特性成熟度速查表
🟡 = Preview(预览) | 🟢 = GA(正式版) | ⚪ = Incubator(孵化)
| 特性 | 首次预览 | 正式版 | 备注 |
|---|---|---|---|
| Lambda 表达式 | --- | Java 8 | --- |
| Stream API | --- | Java 8 | --- |
| Optional | --- | Java 8 | --- |
| 模块化系统 | --- | Java 9 | --- |
局部变量类型推断 var |
--- | Java 10 | --- |
| HTTP 客户端 | Java 9 | Java 11 | --- |
| Switch 表达式 | Java 12 | Java 14 | --- |
| 文本块 | Java 13 | Java 15 | --- |
| Records | Java 14 | Java 16 | --- |
| instanceof 模式匹配 | Java 14 | Java 16 | --- |
| 密封类 | Java 15 | Java 17 | --- |
| 虚拟线程 | Java 19 | Java 21 | Project Loom |
| Switch 模式匹配 | Java 17 | Java 21 | --- |
| Record Patterns | Java 19 | Java 21 | --- |
| 顺序集合 | --- | Java 21 | --- |
未命名模式与变量 _ |
Java 21 | Java 23 | --- |
| 实例 main 方法 | Java 21 | Java 23 | --- |
| 结构化并发 | Java 19⚪ | Java 24 | --- |
| Scoped Values | Java 20⚪ | Java 24 | --- |
| Stream Gatherers | Java 22 | Java 24 | --- |
| 字符串模板 | Java 21🟡 | 已移除 | 重新设计中 |