Java 语言演进:从 Java 7 到 Java 21 那些重大的变革

第一章 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.DateCalendar 可变、非线程安全、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.wsjava.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、equalshashCodetoString)。

语法规则

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 集合框架中,ListLinkedHashSetSortedSet 等都有"有序"的概念,但缺乏统一的接口来访问首/尾元素或反转顺序。

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🟡 已移除 重新设计中