第一章 Java 版本更替

1.1 发布节奏变革

时期发布策略代表版本
2011 年之前不定期大版本Java 5/6/7
2017 年 9 月起每 6 个月固定发布Java 9 → Java 25+
LTS 策略2~3 年指定一个 LTS7, 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 块中手动关闭资源(如流、连接),代码冗长且易出错。

语法规则

// 资源声明在 try() 中,需实现 AutoCloseable 接口
try (ResourceType resource = new ResourceType()) {
    // 使用资源
} catch (ExceptionType e) {
    // 处理异常
}
// 资源自动关闭,无需 finally

对比

// ===== 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)

语法规则

// 使用 | 分隔多种异常类型
try {
    // 可能抛出 IOException 或 SQLException 的代码
} catch (IOException | SQLException e) {
    // 统一处理,e 的类型为两种异常的最近公共父类
    logger.error("操作失败", e);
}

与旧版对比:Java 6 中需为每种异常编写单独的 catch 块,导致大量重复代码。


2.3 菱形运算符(Diamond Operator)

// 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

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 数字字面量增强

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 具备了函数式编程的核心能力。

语法规则

// 基本形式
(parameters) -> expression
(parameters) -> { statements; }

// 类型可省略(编译器推断)
// 单参数可省略括号
// 单表达式可省略大括号和 return

实际示例

// ===== 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)

@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 引入了声明式的数据处理管道。

语法规则

// 流水线模型:Source → Intermediate Operations → Terminal Operation
stream()
    .filter(...)      // 过滤(中间操作,惰性求值)
    .map(...)         // 映射(中间操作,惰性求值)
    .sorted(...)      // 排序(中间操作,有状态)
    .collect(...)     // 收集(终端操作,触发执行)

实际示例

// 需求:找出所有价格 > 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 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 提供了一种显式表达"值可能不存在"的容器类型。

// 创建
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 接口默认方法与静态方法

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)

// 不可变、线程安全的日期时间类
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)

// 四种形式
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

// 异步编程,支持链式组合
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 边界模糊。

语法规则

// 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 — 交互式编程工具

$ jshell
jshell> int x = 10;
x ==> 10
jshell> System.out.println(x * 2);
20
jshell> /list    # 查看历史代码
jshell> /save session.java  # 保存会话

应用场景:快速验证代码片段、教学演示、调试探索。


4.3 集合工厂方法

// 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 接口私有方法

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

// 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.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 的类型系统虽然保证了安全性,但在局部变量声明时存在大量冗余的类型书写。

语法规则

// 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 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()

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 和异步调用。

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 新方法

" 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 11 允许在 Lambda 参数上使用 var(主要用于添加注解)
(@NotNull var x, @NotNull var y) -> x + y

// 必须所有参数都使用 var 或都不使用

6.4 单文件源代码运行

# 无需编译,直接运行
$ java HelloWorld.java

6.5 其他重要变更

特性说明
ZGC(实验性)低延迟垃圾回收器
Flight Recorder开源(原为 Oracle JDK 商业特性)
移除 Java EE 模块java.xml.wsjava.xml.bind 等被移除
移除 NashornJavaScript 引擎被标记为弃用

第七章 Java 12~13 — Switch 表达式与文本块预览

7.1 Switch 表达式(Java 12 预览 / Java 13 第二预览 / Java 14 正式版)

背景:传统 switch 语句存在诸多缺陷:fall-through 语义容易出错、不能作为表达式返回值、不支持模式匹配。

// ===== 传统 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 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)。

语法规则

// 一行定义完整的数据载体类
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]

高级特性

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 对比

特性RecordLombok @Data
标准化语言级特性第三方注解处理器
可变性不可变可变
继承不可继承可继承
getter 命名name()getName()

8.2 NullPointerException 增强诊断(Java 14)

// 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 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(完全禁止继承)或完全开放(任何人都可以继承)。缺乏中间地带来控制"谁可以继承我"。

语法规则

// 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+)

// 密封类 + 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 服务器
# 命令行启动静态文件服务器
$ 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)
// 将多个并发子任务绑定为一个单元,统一生命周期管理
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)
// 在 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)
// 替代 ThreadLocal 的更安全方案
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

ScopedValue.runWhere(CURRENT_USER, user, () -> {
    processRequest();  // 在此范围内可通过 CURRENT_USER.get() 获取值
});
unnamed 模式与变量(Preview)
// 使用 _ 忽略不需要的变量
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 的核心成果,被誉为"高并发游戏规则改变者"。

语法规则

// 方式一:直接创建虚拟线程
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 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(守卫模式)

// 在 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                     -> "其他";
};

与密封类的完美配合

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,直接访问其组件。

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

// 新增三个接口
// 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 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)

# 启用分代 ZGC
-XX:+UseZGC -XX:+ZGenerational

将堆分为年轻代和老年代,显著降低 GC 停顿时间(亚毫秒级),提升吞吐量。


11.6 Unnamed Classes 与 Instance Main Methods(Preview)

// 无需类声明、无需 static、无需 String[] args
void main() {
    System.out.println("Hello, World!");
}

设计目标:降低 Java 入门门槛,让初学者从第一行代码就能运行程序。


11.7 String Templates(Preview — JEP 430)

// 字符串模板(类似 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 APIJava 8
OptionalJava 8
模块化系统Java 9
局部变量类型推断 varJava 10
HTTP 客户端Java 9Java 11
Switch 表达式Java 12Java 14
文本块Java 13Java 15
RecordsJava 14Java 16
instanceof 模式匹配Java 14Java 16
密封类Java 15Java 17
虚拟线程Java 19Java 21Project Loom
Switch 模式匹配Java 17Java 21
Record PatternsJava 19Java 21
顺序集合Java 21
未命名模式与变量 _Java 21Java 23
实例 main 方法Java 21Java 23
结构化并发Java 19⚪Java 24
Scoped ValuesJava 20⚪Java 24
Stream GatherersJava 22Java 24
字符串模板Java 21🟡已移除重新设计中
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐