JDK 8 到 JDK 24 更新内容详解

本文档详细介绍从 JDK 8 到 JDK 24 的所有重大更新内容和新特性。

目录


一、版本更新概览

JDK 8 (2014.03) - LTS ⭐

  • Lambda 表达式 - 函数式编程基础
  • Stream API - 集合流式处理
  • Optional - 优雅处理空值
  • 新日期时间 API - java.time
  • 默认方法 - 接口可以有默认实现
  • 方法引用 - :: 操作符
  • Nashorn JavaScript 引擎

JDK 9 (2017.09)

  • 模块系统 (Jigsaw) - module-info.java
  • JShell - 交互式 REPL 工具
  • 集合工厂方法 - List.of(), Set.of(), Map.of()
  • 接口私有方法
  • Stream API 增强 - takeWhile, dropWhile, ofNullable
  • Optional 增强 - ifPresentOrElse, or, stream
  • Process API 改进
  • HTTP/2 Client (孵化)

JDK 10 (2018.03)

  • 局部变量类型推断 - var 关键字
  • G1 GC 并行 Full GC
  • 应用类数据共享 (Application CDS)
  • 线程局部握手
  • Optional.orElseThrow() 无参版本

JDK 11 (2018.09) - LTS ⭐

  • HTTP Client 正式版 - 支持 HTTP/2 和 WebSocket
  • String 新方法 - isBlank(), lines(), strip(), repeat()
  • Files 新方法 - readString(), writeString()
  • Lambda 中使用 var
  • 单文件源码运行 - java Hello.java
  • 移除 Java EE 和 CORBA 模块
  • ZGC (实验性) - 低延迟垃圾收集器
  • Flight Recorder 开源

JDK 12 (2019.03)

  • Switch 表达式 (预览)
  • Shenandoah GC (实验性)
  • JVM 常量 API
  • 微基准测试套件
  • G1 可中断 Mixed GC

JDK 13 (2019.09)

  • 文本块 (预览) - """多行字符串"""
  • Switch 表达式 (第二次预览)
  • ZGC 改进 - 返还未使用内存
  • Socket API 重新实现
  • 动态 CDS 归档

JDK 14 (2020.03)

  • Switch 表达式 (正式)
  • instanceof 模式匹配 (预览) - if (obj instanceof String s)
  • Records (预览) - 不可变数据类
  • NullPointerException 增强 - 更详细的错误信息
  • 文本块 (第二次预览)
  • G1 支持 NUMA
  • 移除 CMS 垃圾收集器
  • ZGC 支持 macOS 和 Windows

JDK 15 (2020.09)

  • 文本块 (正式)
  • Sealed Classes (预览) - 密封类
  • Hidden Classes - 隐藏类
  • Records (第二次预览)
  • instanceof 模式匹配 (第二次预览)
  • ZGC 和 Shenandoah 正式发布
  • 移除 Nashorn JavaScript 引擎
  • Edwards-Curve 数字签名算法

JDK 16 (2021.03)

  • Records (正式)
  • instanceof 模式匹配 (正式)
  • Sealed Classes (第二次预览)
  • Vector API (孵化) - SIMD 支持
  • Foreign Linker API (孵化)
  • 打包工具 jpackage
  • Unix-Domain Socket Channels
  • Alpine Linux 移植

JDK 17 (2021.09) - LTS ⭐

  • Sealed Classes (正式)
  • Pattern Matching for switch (预览)
  • 强封装 JDK 内部 API
  • 移除 RMI Activation
  • 移除实验性 AOT 和 JIT 编译器
  • 弃用 Security Manager
  • Foreign Function & Memory API (孵化)
  • Context-Specific Deserialization Filters
  • macOS/AArch64 移植

JDK 18 (2022.03)

  • 默认 UTF-8 字符集
  • 简单 Web 服务器 - jwebserver 命令
  • 代码片段 API - @snippet JavaDoc 标签
  • Pattern Matching for switch (第二次预览)
  • Vector API (第三次孵化)
  • 互联网地址解析 SPI
  • 弃用 Finalization

JDK 19 (2022.09)

  • 虚拟线程 (预览) - Project Loom
  • 结构化并发 (孵化)
  • Record Patterns (预览)
  • Pattern Matching for switch (第三次预览)
  • Foreign Function & Memory API (预览)
  • Vector API (第四次孵化)

JDK 20 (2023.03)

  • 虚拟线程 (第二次预览)
  • 结构化并发 (第二次孵化)
  • Scoped Values (孵化) - 线程局部变量替代方案
  • Record Patterns (第二次预览)
  • Pattern Matching for switch (第四次预览)
  • Foreign Function & Memory API (第二次预览)
  • Vector API (第五次孵化)

JDK 21 (2023.09) - LTS ⭐

  • 虚拟线程 (正式) 🎉 - 轻量级线程,大幅简化并发编程
  • Record Patterns (正式)
  • Pattern Matching for switch (正式)
  • Sequenced Collections - 有序集合新接口
  • String Templates (预览) - 字符串模板 STR."Hello \{name}"
  • Unnamed Patterns and Variables (预览) - _ 占位符
  • Unnamed Classes and Instance Main Methods (预览)
  • 结构化并发 (预览)
  • Scoped Values (预览)
  • Foreign Function & Memory API (第三次预览)
  • Vector API (第六次孵化)
  • Generational ZGC
  • 弃用 Windows 32位 x86 移植

JDK 22 (2024.03)

  • Unnamed Variables & Patterns (正式) - _ 占位符正式可用
  • String Templates (第二次预览)
  • Statements before super() (预览) - 构造函数中 super() 前可执行语句
  • Foreign Function & Memory API (正式) 🎉
  • Stream Gatherers (预览) - 自定义中间操作
  • Class-File API (预览) - 解析和生成 class 文件
  • Launch Multi-File Source-Code Programs
  • 结构化并发 (第二次预览)
  • Scoped Values (第二次预览)
  • Vector API (第七次孵化)
  • Region Pinning for G1

JDK 23 (2024.09)

  • Primitive Types in Patterns (预览) - switch 支持原始类型
  • Module Import Declarations (预览) - import module java.base
  • Implicitly Declared Classes (第二次预览)
  • Flexible Constructor Bodies (第二次预览)
  • Stream Gatherers (第二次预览)
  • Class-File API (第二次预览)
  • 结构化并发 (第三次预览)
  • Scoped Values (第三次预览)
  • Vector API (第八次孵化)
  • Markdown 文档注释
  • ZGC 默认使用分代模式

JDK 24 (2025.03)

  • Stream Gatherers (正式) 🎉
  • Class-File API (正式) 🎉
  • Primitive Types in Patterns (第二次预览)
  • Module Import Declarations (第二次预览)
  • Flexible Constructor Bodies (第三次预览)
  • 结构化并发 (第四次预览)
  • Scoped Values (第四次预览)
  • 移除 String Templates ❌ - 已从预览中移除,将重新设计
  • Quantum-Resistant 加密算法
  • JIT 编译后台优化改进

二、语言特性详解

1. Lambda 表达式 (JDK 8)

Lambda 是 Java 函数式编程的基础,允许将函数作为参数传递。

// 传统匿名内部类
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda 表达式
Runnable r2 = () -> System.out.println("Hello");

// 带参数的 Lambda
Comparator comp = (s1, s2) -> s1.compareTo(s2);

// 方法引用 - 更简洁
Comparator comp2 = String::compareTo;

函数式接口:只有一个抽象方法的接口

@FunctionalInterface
public interface MyFunction {
    R apply(T t);
}

// 常用内置函数式接口
Function len = String::length;
Predicate isEmpty = String::isEmpty;
Consumer print = System.out::println;
Supplier supplier = () -> "Hello";
BiFunction concat = String::concat;

2. Stream API (JDK 8+)

流式处理集合数据,支持函数式、链式操作。

List names = List.of("Alice", "Bob", "Charlie", "David");

// 基本操作
List result = names.stream()
    .filter(n -> n.length() > 3)        // 过滤
    .map(String::toUpperCase)            // 转换
    .sorted()                            // 排序
    .distinct()                          // 去重
    .limit(10)                           // 限制数量
    .skip(2)                             // 跳过
    .collect(Collectors.toList());       // 收集结果

// 聚合操作
long count = names.stream().count();
Optional first = names.stream().findFirst();
boolean allMatch = names.stream().allMatch(n -> n.length() > 2);
String joined = names.stream().collect(Collectors.joining(", "));

// 分组
Map> byLength = names.stream()
    .collect(Collectors.groupingBy(String::length));

// 并行流 - 多核处理
List parallelResult = names.parallelStream()
    .filter(n -> n.length() > 3)
    .collect(Collectors.toList());

// reduce 归约
int sum = List.of(1, 2, 3, 4, 5).stream()
    .reduce(0, Integer::sum);

// flatMap - 扁平化
List> nested = List.of(List.of(1, 2), List.of(3, 4));
List flat = nested.stream()
    .flatMap(List::stream)
    .toList();  // [1, 2, 3, 4]

JDK 9 Stream 增强

// takeWhile - 取满足条件的前缀
Stream.of(1, 2, 3, 4, 5, 1, 2)
    .takeWhile(n -> n < 4)
    .toList();  // [1, 2, 3]

// dropWhile - 丢弃满足条件的前缀
Stream.of(1, 2, 3, 4, 5, 1, 2)
    .dropWhile(n -> n < 4)
    .toList();  // [4, 5, 1, 2]

// ofNullable - 处理可能为 null 的值
Stream.ofNullable(null).count();  // 0
Stream.ofNullable("hi").count();  // 1

// iterate 带终止条件
Stream.iterate(1, n -> n < 100, n -> n * 2)
    .toList();  // [1, 2, 4, 8, 16, 32, 64]

3. Optional (JDK 8+)

优雅处理可能为 null 的值,避免 NullPointerException。

// 创建 Optional
Optional empty = Optional.empty();
Optional of = Optional.of("Hello");        // 不能为 null
Optional nullable = Optional.ofNullable(null);  // 可以为 null

// 基本操作
String value = optional.orElse("default");
String value2 = optional.orElseGet(() -> expensiveComputation());
String value3 = optional.orElseThrow();  // JDK 10+
String value4 = optional.orElseThrow(() -> new RuntimeException("Not found"));

// 条件操作
optional.ifPresent(System.out::println);
optional.ifPresentOrElse(                  // JDK 9+
    System.out::println,
    () -> System.out.println("Empty")
);

// 转换和过滤
Optional length = optional
    .filter(s -> s.length() > 3)
    .map(String::length)
    .flatMap(this::findById);

// JDK 9: or() - 提供替代 Optional
Optional result = optional.or(() -> Optional.of("alternative"));

// JDK 9: stream() - 转为 Stream
List list = optional.stream().toList();

// 链式处理
String result = Optional.ofNullable(user)
    .map(User::getAddress)
    .map(Address::getCity)
    .map(City::getName)
    .orElse("Unknown");

4. 新日期时间 API (JDK 8)

java.time 包,不可变、线程安全。

// 日期
LocalDate date = LocalDate.now();
LocalDate date2 = LocalDate.of(2025, 1, 7);
LocalDate date3 = LocalDate.parse("2025-01-07");

// 时间
LocalTime time = LocalTime.now();
LocalTime time2 = LocalTime.of(14, 30, 0);

// 日期时间
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime dateTime2 = LocalDateTime.of(date, time);

// 带时区
ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
Instant instant = Instant.now();  // UTC 时间戳

// 时间间隔
Duration duration = Duration.between(time1, time2);
Period period = Period.between(date1, date2);

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH🇲🇲ss");
String formatted = dateTime.format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2025-01-07 14:30:00", formatter);

// 计算
LocalDate tomorrow = date.plusDays(1);
LocalDate lastMonth = date.minusMonths(1);
LocalDate firstDayOfMonth = date.withDayOfMonth(1);
LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

5. var 局部变量类型推断 (JDK 10)

编译器自动推断局部变量类型。

// 基本使用
var list = new ArrayList();      // ArrayList
var map = Map.of("key", "value");        // Map
var stream = list.stream();               // Stream

// for 循环
for (var item : list) {
    System.out.println(item);
}

for (var i = 0; i < 10; i++) {
    // ...
}

// try-with-resources
try (var reader = new BufferedReader(new FileReader("file.txt"))) {
    var line = reader.readLine();
}

// Lambda 参数 (JDK 11)
BiFunction concat = (var a, var b) -> a + b;
// 可以加注解
Function upper = (@NonNull var s) -> s.toUpperCase();

// ⚠️ 不能使用的场景
var x;                    // 错误:没有初始化
var y = null;             // 错误:无法推断类型
var z = () -> {};         // 错误:Lambda 需要目标类型
var arr = {1, 2, 3};      // 错误:数组初始化需要类型

// 字段和方法参数不能使用 var
class MyClass {
    var field = 1;              // 错误
    void method(var param) {}   // 错误
}

6. 文本块 Text Blocks (JDK 15)

多行字符串字面量,保留格式。

// 传统方式
String json = "{\n" +
    "  \"name\": \"John\",\n" +
    "  \"age\": 30\n" +
    "}";

// 文本块
String json = """
    {
      "name": "John",
      "age": 30
    }
    """;

// HTML
String html = """
    
        
            <h1>Hello, World!</h1>
        
    
    """;

// SQL
String sql = """
    SELECT id, name, email
    FROM users
    WHERE status = 'active'
    ORDER BY name
    """;

// 转义序列
String s1 = """
    line1
    line2\
    still line2
    """;  // \结尾表示不换行

String s2 = """
    col1\scol2\scol3
    """;  // \s 表示空格

// 格式化
String formatted = """
    Name: %s
    Age: %d
    """.formatted("John", 30);

7. Records 记录类 (JDK 16)

不可变数据载体,自动生成构造器、getter、equals、hashCode、toString。

// 定义 Record
public record Point(int x, int y) {}

// 等价于以下传统类
public final class Point {
    private final int x;
    private final int y;
    
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public int x() { return x; }
    public int y() { return y; }
    
    @Override
    public boolean equals(Object o) { /* ... */ }
    
    @Override
    public int hashCode() { /* ... */ }
    
    @Override
    public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}

// 使用
Point p = new Point(10, 20);
int x = p.x();  // 访问器方法,不是 getX()
System.out.println(p);  // Point[x=10, y=20]

// 紧凑构造器 - 验证
public record Person(String name, int age) {
    public Person {  // 紧凑构造器,参数隐式
        if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
        name = name.trim();  // 可以修改参数
    }
}

// 自定义构造器
public record Point(int x, int y) {
    public Point(int xy) {
        this(xy, xy);  // 必须调用规范构造器
    }
}

// 静态方法和实例方法
public record Rectangle(Point topLeft, Point bottomRight) {
    public static Rectangle of(int x1, int y1, int x2, int y2) {
        return new Rectangle(new Point(x1, y1), new Point(x2, y2));
    }
    
    public int width() {
        return bottomRight.x() - topLeft.x();
    }
}

// 实现接口
public record NamedPoint(String name, int x, int y) implements Serializable {}

// 泛型 Record
public record Pair(T first, U second) {}

8. Sealed Classes 密封类 (JDK 17)

限制哪些类可以继承或实现。

// 密封类
public sealed class Shape 
    permits Circle, Rectangle, Triangle {
}

// 允许的子类必须是 final、sealed 或 non-sealed
public final class Circle extends Shape {
    private final double radius;
    public Circle(double radius) { this.radius = radius; }
}

public sealed class Rectangle extends Shape 
    permits Square {
    // Rectangle 也是密封的,只允许 Square 继承
}

public final class Square extends Rectangle {}

public non-sealed class Triangle extends Shape {
    // non-sealed 允许任何类继承
}

// 密封接口
public sealed interface Vehicle 
    permits Car, Truck, Motorcycle {
}

public record Car(String model) implements Vehicle {}
public record Truck(int capacity) implements Vehicle {}
public final class Motorcycle implements Vehicle {}

// 与 Pattern Matching 结合使用
public double area(Shape shape) {
    return switch (shape) {
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Triangle t -> 0.5 * t.base() * t.height();
        // 编译器知道所有可能的子类,无需 default
    };
}

9. Pattern Matching 模式匹配

9.1 instanceof 模式匹配 (JDK 16)

// 传统方式
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// 模式匹配
if (obj instanceof String s) {
    System.out.println(s.length());  // s 直接可用
}

// 作用域
if (obj instanceof String s && s.length() > 5) {
    // s 在整个 if 块中可用
}

// 否定模式
if (!(obj instanceof String s)) {
    return;
}
// s 在这里可用(因为上面 return 了)
System.out.println(s.length());

9.2 Switch 表达式 (JDK 14)

// 传统 switch 语句
int numLetters;
switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        numLetters = 6;
        break;
    case TUESDAY:
        numLetters = 7;
        break;
    // ...
    default:
        throw new IllegalStateException();
}

// Switch 表达式 - 箭头语法
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};

// yield 返回值(多语句块)
String result = switch (status) {
    case 1 -> "One";
    case 2 -> {
        log("Processing two");
        yield "Two";
    }
    default -> "Unknown";
};

9.3 Switch 模式匹配 (JDK 21)

// 类型模式
String describe(Object obj) {
    return switch (obj) {
        case Integer i -> "Integer: " + i;
        case Long l -> "Long: " + l;
        case Double d -> "Double: " + d;
        case String s -> "String: " + s;
        case null -> "Null";
        default -> "Unknown: " + obj.getClass();
    };
}

// 守卫条件 (when)
String categorize(Object obj) {
    return switch (obj) {
        case Integer i when i > 0 -> "Positive integer";
        case Integer i when i < 0 -> "Negative integer";
        case Integer i -> "Zero";
        case String s when s.isEmpty() -> "Empty string";
        case String s -> "String: " + s;
        default -> "Other";
    };
}

// Record 模式
record Point(int x, int y) {}
record Circle(Point center, int radius) {}

String describe(Object obj) {
    return switch (obj) {
        case Point(int x, int y) -> "Point at (" + x + ", " + y + ")";
        case Circle(Point(int x, int y), int r) -> 
            "Circle at (" + x + ", " + y + ") with radius " + r;
        default -> "Unknown";
    };
}

// 嵌套解构
record Pair(T first, U second) {}

void process(Object obj) {
    switch (obj) {
        case Pair(String s, Integer i) -> 
            System.out.println("String-Integer pair: " + s + ", " + i);
        case Pair(Pair(String a, String b), Integer i) -> 
            System.out.println("Nested: " + a + ", " + b + ", " + i);
        default -> {}
    }
}

9.4 Record Patterns (JDK 21)

record Point(int x, int y) {}
record Rectangle(Point topLeft, Point bottomRight) {}

// 解构 Record
void printPoint(Object obj) {
    if (obj instanceof Point(int x, int y)) {
        System.out.println("x = " + x + ", y = " + y);
    }
}

// 嵌套解构
void printRectangle(Object obj) {
    if (obj instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
        System.out.println("From (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
    }
}

// 泛型 Record 解构
record Box(T value) {}

void unbox(Box box) {
    if (box instanceof Box(String s)) {
        System.out.println("Contains: " + s);
    }
}

10. Unnamed Variables 未命名变量 (JDK 22)

使用 _ 表示不需要使用的变量。

// 忽略不需要的变量
try {
    // ...
} catch (Exception _) {  // 不使用异常变量
    System.out.println("An error occurred");
}

// for-each 循环
int count = 0;
for (var _ : list) {  // 只需要计数
    count++;
}

// Lambda
map.forEach((_, value) -> System.out.println(value));

// 模式匹配中忽略部分
record Point(int x, int y) {}

if (obj instanceof Point(int x, _)) {  // 只关心 x
    System.out.println("x = " + x);
}

switch (obj) {
    case Point(int x, _) when x > 0 -> System.out.println("Positive x");
    case Point(_, int y) when y > 0 -> System.out.println("Positive y");
    default -> {}
}

// try-with-resources
try (var _ = ScopedContext.open()) {
    // 只需要资源在作用域内,不需要引用它
}

11. 集合工厂方法 (JDK 9)

创建不可变集合的便捷方法。

// List.of()
List list = List.of("a", "b", "c");
// list.add("d");  // 抛出 UnsupportedOperationException

// Set.of()
Set set = Set.of(1, 2, 3);

// Map.of() - 最多 10 个键值对
Map map = Map.of(
    "one", 1,
    "two", 2,
    "three", 3
);

// Map.ofEntries() - 更多键值对
Map map2 = Map.ofEntries(
    Map.entry("one", 1),
    Map.entry("two", 2),
    Map.entry("three", 3),
    Map.entry("four", 4)
    // ... 可以更多
);

// 复制为不可变集合 (JDK 10)
List immutableCopy = List.copyOf(mutableList);
Set immutableSet = Set.copyOf(mutableSet);
Map immutableMap = Map.copyOf(mutableMap);

// Collectors.toUnmodifiableList/Set/Map (JDK 10)
List result = stream.collect(Collectors.toUnmodifiableList());

// 直接 toList() (JDK 16)
List result = stream.toList();  // 返回不可变列表

12. Sequenced Collections (JDK 21)

为有序集合提供统一接口。

// 新接口层次
// SequencedCollection extends Collection
// SequencedSet extends Set, SequencedCollection
// SequencedMap extends Map

// 获取首尾元素
SequencedCollection seq = new LinkedHashSet<>();
seq.addFirst("first");
seq.addLast("last");
String first = seq.getFirst();
String last = seq.getLast();
seq.removeFirst();
seq.removeLast();

// 反转视图
SequencedCollection reversed = seq.reversed();

// SequencedMap
SequencedMap map = new LinkedHashMap<>();
map.putFirst("first", 1);
map.putLast("last", 99);
Map.Entry firstEntry = map.firstEntry();
Map.Entry lastEntry = map.lastEntry();
map.pollFirstEntry();
map.pollLastEntry();
SequencedMap reversedMap = map.reversed();

// 现有类自动实现
// List -> SequencedCollection
// LinkedHashSet, TreeSet -> SequencedSet
// LinkedHashMap, TreeMap -> SequencedMap

三、并发编程特性

13. 虚拟线程 Virtual Threads (JDK 21)

轻量级线程,解决"一请求一线程"模型的可扩展性问题。

// 创建虚拟线程
Thread vThread = Thread.ofVirtual().start(() -> {
    System.out.println("Running in virtual thread");
});

// 带名称的虚拟线程
Thread.ofVirtual()
    .name("my-virtual-thread")
    .start(() -> doSomething());

// 虚拟线程工厂
ThreadFactory factory = Thread.ofVirtual().factory();

// 使用 Executors
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    // 每个任务一个虚拟线程
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}  // 自动等待所有任务完成并关闭

// 传统平台线程对比
Thread platformThread = Thread.ofPlatform()
    .name("platform-thread")
    .start(() -> doSomething());

// 检查是否是虚拟线程
boolean isVirtual = Thread.currentThread().isVirtual();

// 最佳实践
// ✅ 适合 I/O 密集型任务
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    List> futures = urls.stream()
        .map(url -> executor.submit(() -> fetchUrl(url)))
        .toList();
    
    for (var future : futures) {
        System.out.println(future.get());
    }
}

// ⚠️ 注意事项
// 1. 不要池化虚拟线程(它们本身就是轻量的)
// 2. 避免在虚拟线程中使用 synchronized(会固定到平台线程)
// 3. 优先使用 ReentrantLock 替代 synchronized
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区
} finally {
    lock.unlock();
}

虚拟线程 vs 平台线程

特性平台线程虚拟线程
创建成本高(~1MB 栈空间)低(~几KB)
数量限制数千个数百万个
调度OS 调度JVM 调度
阻塞代价高(浪费 OS 线程)低(自动挂起)
适用场景CPU 密集型I/O 密集型

14. 结构化并发 Structured Concurrency (JDK 21 预览)

将并发任务结构化,确保任务生命周期与代码块对齐。

// 结构化并发
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        // 并行执行多个任务
        Supplier user = scope.fork(() -> findUser());
        Supplier order = scope.fork(() -> fetchOrder());
        
        scope.join();           // 等待所有任务完成
        scope.throwIfFailed();  // 如果有任务失败则抛出异常
        
        // 所有任务成功,获取结果
        return new Response(user.get(), order.get());
    }
}

// ShutdownOnSuccess - 任意一个成功就返回
 T race(List> tasks) throws Exception {
    try (var scope = new StructuredTaskScope.ShutdownOnSuccess()) {
        for (var task : tasks) {
            scope.fork(task);
        }
        scope.join();
        return scope.result();  // 返回第一个成功的结果
    }
}

// 自定义策略
class CollectingScope extends StructuredTaskScope {
    private final Collection results = new ConcurrentLinkedQueue<>();
    private final Collection errors = new ConcurrentLinkedQueue<>();
    
    @Override
    protected void handleComplete(Subtask subtask) {
        switch (subtask.state()) {
            case SUCCESS -> results.add(subtask.get());
            case FAILED -> errors.add(subtask.exception());
            case UNAVAILABLE -> {}
        }
    }
    
    public Collection results() { return results; }
    public Collection errors() { return errors; }
}

15. Scoped Values (JDK 21 预览)

线程局部变量的现代替代方案,适合虚拟线程。

// 定义 ScopedValue
private static final ScopedValue CURRENT_USER = ScopedValue.newInstance();

// 绑定值并在作用域内使用
void handleRequest(User user) {
    ScopedValue.where(CURRENT_USER, user).run(() -> {
        // 在这个作用域内,CURRENT_USER 绑定到 user
        processRequest();
    });
}

void processRequest() {
    // 获取当前作用域的值
    User user = CURRENT_USER.get();
    // 或检查是否绑定
    if (CURRENT_USER.isBound()) {
        User user = CURRENT_USER.get();
    }
    // 带默认值
    User user = CURRENT_USER.orElse(DEFAULT_USER);
}

// 嵌套绑定
ScopedValue.where(CURRENT_USER, user1).run(() -> {
    System.out.println(CURRENT_USER.get());  // user1
    
    ScopedValue.where(CURRENT_USER, user2).run(() -> {
        System.out.println(CURRENT_USER.get());  // user2
    });
    
    System.out.println(CURRENT_USER.get());  // user1
});

// 与结构化并发结合
void handle() {
    ScopedValue.where(CURRENT_USER, user).run(() -> {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            // 子任务自动继承 ScopedValue
            scope.fork(() -> {
                User u = CURRENT_USER.get();  // 可以访问
                return processA(u);
            });
            scope.fork(() -> {
                User u = CURRENT_USER.get();  // 可以访问
                return processB(u);
            });
            scope.join();
        }
    });
}

ScopedValue vs ThreadLocal

特性ThreadLocalScopedValue
可变性可变不可变
生命周期显式 remove自动作用域
继承InheritableThreadLocal自动继承(结构化并发)
内存可能泄漏自动释放
虚拟线程每个线程都复制高效共享

四、API 和工具改进

16. HTTP Client (JDK 11)

现代化的 HTTP 客户端,支持 HTTP/2 和 WebSocket。

// 创建 HttpClient
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .connectTimeout(Duration.ofSeconds(10))
    .followRedirects(HttpClient.Redirect.NORMAL)
    .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 8080)))
    .authenticator(Authenticator.getDefault())
    .build();

// 同步 GET 请求
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/data"))
    .header("Accept", "application/json")
    .GET()
    .build();

HttpResponse response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

int status = response.statusCode();
String body = response.body();
HttpHeaders headers = response.headers();

// 异步请求
CompletableFuture> futureResponse = 
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

futureResponse.thenAccept(resp -> {
    System.out.println("Status: " + resp.statusCode());
    System.out.println("Body: " + resp.body());
});

// POST 请求
HttpRequest postRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString("""
        {"name": "John", "email": "john@example.com"}
        """))
    .build();

// 文件上传
HttpRequest uploadRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/upload"))
    .POST(HttpRequest.BodyPublishers.ofFile(Path.of("file.txt")))
    .build();

// 响应处理器
HttpResponse fileResponse = client.send(request,
    HttpResponse.BodyHandlers.ofFile(Path.of("response.txt")));

HttpResponse streamResponse = client.send(request,
    HttpResponse.BodyHandlers.ofInputStream());

HttpResponse> linesResponse = client.send(request,
    HttpResponse.BodyHandlers.ofLines());

// 并发请求
List requests = urls.stream()
    .map(url -> HttpRequest.newBuilder(URI.create(url)).build())
    .toList();

List>> futures = requests.stream()
    .map(req -> client.sendAsync(req, HttpResponse.BodyHandlers.ofString()))
    .toList();

CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();

17. String 新方法

// JDK 11
"  hello  ".strip();        // "hello" (Unicode 感知的 trim)
"  hello  ".stripLeading(); // "hello  "
"  hello  ".stripTrailing();// "  hello"
"".isBlank();               // true
"   ".isBlank();            // true
"hello\nworld".lines();     // Stream: ["hello", "world"]
"ha".repeat(3);             // "hahaha"

// JDK 12
"hello".indent(4);          // "    hello\n"
"  hello".indent(-2);       // "hello\n"
"hello".transform(s -> s.toUpperCase());  // "HELLO"

// JDK 15 (文本块相关)
"hello\nworld".stripIndent();    // 移除公共缩进
"hello\\nworld".translateEscapes(); // 处理转义: "hello\nworld"

// formatted (JDK 15)
"Name: %s, Age: %d".formatted("John", 30);  // "Name: John, Age: 30"

18. Files 新方法

// JDK 11
// 读取整个文件为字符串
String content = Files.readString(Path.of("file.txt"));
String content = Files.readString(Path.of("file.txt"), StandardCharsets.UTF_8);

// 写入字符串到文件
Files.writeString(Path.of("file.txt"), "Hello, World!");
Files.writeString(Path.of("file.txt"), "Append", 
    StandardOpenOption.APPEND);

// JDK 12
// mismatch - 查找两个文件第一个不同的位置
long pos = Files.mismatch(path1, path2);  // -1 表示完全相同

19. Process API (JDK 9+)

// 获取当前进程信息
ProcessHandle current = ProcessHandle.current();
long pid = current.pid();
ProcessHandle.Info info = current.info();
Optional command = info.command();
Optional arguments = info.arguments();
Optional startTime = info.startInstant();
Optional cpuDuration = info.totalCpuDuration();

// 获取所有进程
ProcessHandle.allProcesses()
    .filter(p -> p.info().command().isPresent())
    .forEach(p -> System.out.println(p.pid() + ": " + p.info().command().get()));

// 进程树
current.children();    // 直接子进程
current.descendants(); // 所有后代进程
current.parent();      // 父进程

// 等待进程结束
process.onExit().thenAccept(p -> {
    System.out.println("Process " + p.pid() + " exited");
});

// 销毁进程
process.destroy();        // 正常终止
process.destroyForcibly(); // 强制终止

20. Foreign Function & Memory API (JDK 22)

访问本地代码和堆外内存的现代 API,替代 JNI。

// 内存分配
try (Arena arena = Arena.ofConfined()) {
    // 分配 100 字节
    MemorySegment segment = arena.allocate(100);
    
    // 写入数据
    segment.set(ValueLayout.JAVA_INT, 0, 42);
    segment.set(ValueLayout.JAVA_LONG, 4, 123456789L);
    
    // 读取数据
    int value = segment.get(ValueLayout.JAVA_INT, 0);
    
    // 分配数组
    MemorySegment array = arena.allocate(ValueLayout.JAVA_INT, 10);
    for (int i = 0; i < 10; i++) {
        array.setAtIndex(ValueLayout.JAVA_INT, i, i * i);
    }
}  // 自动释放内存

// 调用本地函数
// 假设要调用 C 函数: size_t strlen(const char* str)
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();

MethodHandle strlen = linker.downcallHandle(
    stdlib.find("strlen").orElseThrow(),
    FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);

try (Arena arena = Arena.ofConfined()) {
    MemorySegment str = arena.allocateFrom("Hello");
    long len = (long) strlen.invoke(str);
    System.out.println("Length: " + len);  // 5
}

// 内存映射文件
try (FileChannel channel = FileChannel.open(path, READ, WRITE);
     Arena arena = Arena.ofConfined()) {
    
    MemorySegment mapped = channel.map(READ_WRITE, 0, channel.size(), arena);
    // 直接操作映射内存
    int firstInt = mapped.get(ValueLayout.JAVA_INT, 0);
}

21. Stream Gatherers (JDK 24)

自定义 Stream 中间操作。

// 内置 Gatherers
// 窗口操作
Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.windowFixed(2))
    .toList();  // [[1, 2], [3, 4], [5]]

Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.windowSliding(3))
    .toList();  // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

// fold - 有状态的归约
Stream.of(1, 2, 3, 4, 5)
    .gather(Gatherers.fold(() -> 0, Integer::sum))
    .toList();  // [1, 3, 6, 10, 15] - 运行总和

// scan - 类似 fold 但包含初始值
Stream.of(1, 2, 3)
    .gather(Gatherers.scan(() -> 0, Integer::sum))
    .toList();  // [0, 1, 3, 6]

// mapConcurrent - 并发映射
Stream.of(url1, url2, url3)
    .gather(Gatherers.mapConcurrent(10, this::fetchUrl))
    .toList();

// 自定义 Gatherer
Gatherer doubleOdds = Gatherer.of(
    (state, element, downstream) -> {
        if (element % 2 != 0) {
            return downstream.push(element * 2);
        }
        return true;
    }
);

Stream.of(1, 2, 3, 4, 5)
    .gather(doubleOdds)
    .toList();  // [2, 6, 10]

// 带状态的 Gatherer
Gatherer runningMax = Gatherer.ofSequential(
    () -> new int[]{Integer.MIN_VALUE},  // 初始状态
    (state, element, downstream) -> {
        state[0] = Math.max(state[0], element);
        return downstream.push(state[0]);
    }
);

Stream.of(3, 1, 4, 1, 5, 9, 2, 6)
    .gather(runningMax)
    .toList();  // [3, 3, 4, 4, 5, 9, 9, 9]

五、垃圾收集器演进

22. G1 (Garbage First)

JDK 9 开始成为默认 GC。

# 启用 G1(JDK 9+ 默认)
-XX:+UseG1GC

# 设置期望的最大停顿时间
-XX:MaxGCPauseMillis=200

# 堆区域大小(1MB-32MB,2的幂)
-XX:G1HeapRegionSize=4m

# 并发 GC 线程数
-XX:ConcGCThreads=4

# 触发并发标记的堆占用阈值
-XX:InitiatingHeapOccupancyPercent=45

关键改进

  • JDK 10: 并行 Full GC
  • JDK 12: 可中断 Mixed GC
  • JDK 14: NUMA 感知
  • JDK 22: Region Pinning(JNI 友好)

23. ZGC (Z Garbage Collector)

超低延迟 GC,停顿时间通常 < 1ms。

# 启用 ZGC
-XX:+UseZGC

# 启用分代 ZGC(JDK 21+,JDK 23 默认)
-XX:+UseZGC -XX:+ZGenerational

# 设置堆大小
-Xmx16g

# 并发 GC 线程数
-XX:ConcGCThreads=4

特点

  • 停顿时间不随堆大小增长(可达 TB 级堆)
  • 并发压缩
  • 彩色指针
  • 加载屏障
  • JDK 15 正式发布
  • JDK 21 引入分代模式
  • JDK 23 分代模式成为默认

24. Shenandoah GC

低停顿 GC,由 Red Hat 开发。

# 启用 Shenandoah
-XX:+UseShenandoahGC

# 设置堆大小
-Xmx16g

# GC 启发式策略
-XX:ShenandoahGCHeuristics=adaptive

特点

  • 并发压缩
  • 与 ZGC 类似的低延迟目标
  • JDK 15 正式发布
  • 不在 Oracle JDK 中(在 OpenJDK 中)

六、其他重要改进

25. 接口私有方法 (JDK 9)

public interface MyInterface {
    default void publicMethod() {
        privateHelper();
    }
    
    private void privateHelper() {
        // 私有方法实现
    }
    
    private static void privateStaticHelper() {
        // 静态私有方法
    }
}

26. try-with-resources 改进 (JDK 9)

// JDK 7 - 必须在 try 中声明
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
    // ...
}

// JDK 9 - 可以使用 effectively final 变量
BufferedReader reader = new BufferedReader(new FileReader(file));
try (reader) {  // reader 是 effectively final
    // ...
}

// 多个资源
BufferedReader reader1 = new BufferedReader(new FileReader(file1));
BufferedReader reader2 = new BufferedReader(new FileReader(file2));
try (reader1; reader2) {
    // ...
}

27. NullPointerException 增强 (JDK 14)

// 传统错误信息
a.b.c.d = 10;
// NullPointerException: null

// JDK 14+ 详细信息
// NullPointerException: Cannot read field "c" because "a.b" is null

// 启用(JDK 14 需要显式启用,JDK 15+ 默认)
// -XX:+ShowCodeDetailsInExceptionMessages

28. 模块系统 (JDK 9)

// module-info.java
module com.myapp {
    // 依赖其他模块
    requires java.sql;
    requires transitive java.logging;  // 传递依赖
    
    // 导出包
    exports com.myapp.api;
    exports com.myapp.internal to com.myapp.plugin;  // 限定导出
    
    // 开放包供反射
    opens com.myapp.model;
    opens com.myapp.internal to com.fasterxml.jackson.databind;
    
    // 使用服务
    uses com.myapp.spi.Plugin;
    
    // 提供服务实现
    provides com.myapp.spi.Plugin with 
        com.myapp.impl.DefaultPlugin,
        com.myapp.impl.AdvancedPlugin;
}

// 编译和运行
// javac -d out --module-source-path src $(find src -name "*.java")
// java --module-path out -m com.myapp/com.myapp.Main

29. JShell (JDK 9)

交互式 Java REPL。

$ jshell

jshell> int x = 10
x ==> 10

jshell> String greeting = "Hello"
greeting ==> "Hello"

jshell> greeting.toUpperCase()
$3 ==> "HELLO"

jshell> void sayHello(String name) {
   ...>     System.out.println("Hello, " + name);
   ...> }
|  created method sayHello(String)

jshell> sayHello("World")
Hello, World

jshell> /list
   1 : int x = 10;
   2 : String greeting = "Hello";
   3 : greeting.toUpperCase()
   4 : void sayHello(String name) { ... }
   5 : sayHello("World")

jshell> /exit

30. jpackage 打包工具 (JDK 16)

创建平台特定的安装包。

# 创建应用镜像
jpackage --type app-image \
    --name MyApp \
    --input target/libs \
    --main-jar myapp.jar \
    --main-class com.myapp.Main

# 创建安装包
# Windows: exe, msi
jpackage --type msi --name MyApp ...

# macOS: dmg, pkg
jpackage --type dmg --name MyApp ...

# Linux: deb, rpm
jpackage --type deb --name MyApp ...

# 更多选项
jpackage \
    --type msi \
    --name "My Application" \
    --app-version 1.0.0 \
    --vendor "My Company" \
    --description "My awesome application" \
    --icon app.ico \
    --input target/libs \
    --main-jar myapp.jar \
    --main-class com.myapp.Main \
    --java-options "-Xmx512m" \
    --win-menu \
    --win-shortcut

七、总结

关键里程碑

版本最重要特性
JDK 8Lambda、Stream、Optional
JDK 9模块系统
JDK 10var 类型推断
JDK 11HTTP Client、String 增强
JDK 14Switch 表达式、NPE 增强
JDK 15文本块
JDK 16Records、instanceof 模式匹配
JDK 17Sealed Classes
JDK 21虚拟线程、Pattern Matching for switch
JDK 22Foreign Function & Memory API
JDK 24Stream Gatherers、Class-File API

版本演进时间线

JDK 8  (2014.03) ─── Lambda │ Stream │ Optional │ Date/Time API
       │
JDK 9  (2017.09) ─── Module System │ JShell │ Collection Factories
       │
JDK 10 (2018.03) ─── var (局部变量类型推断)
       │
JDK 11 (2018.09) LTS ─── HTTP Client │ String 增强 │ ZGC (实验)
       │
JDK 12 (2019.03) ─── Switch 表达式 (预览)
       │
JDK 13 (2019.09) ─── 文本块 (预览)
       │
JDK 14 (2020.03) ─── Switch 表达式 │ NPE 增强 │ Records (预览)
       │
JDK 15 (2020.09) ─── Text Blocks │ Sealed Classes (预览) │ ZGC 正式
       │
JDK 16 (2021.03) ─── Records │ instanceof Pattern │ jpackage
       │
JDK 17 (2021.09) LTS ─── Sealed Classes │ Pattern Matching Switch (预览)
       │
JDK 18 (2022.03) ─── 默认 UTF-8 │ 简单 Web 服务器
       │
JDK 19 (2022.09) ─── 虚拟线程 (预览) │ 结构化并发 (孵化)
       │
JDK 20 (2023.03) ─── 虚拟线程 (第二次预览) │ Scoped Values (孵化)
       │
JDK 21 (2023.09) LTS ─── Virtual Threads │ Record Patterns │ Sequenced Collections
       │
JDK 22 (2024.03) ─── Unnamed Variables │ FFM API │ Stream Gatherers (预览)
       │
JDK 23 (2024.09) ─── 分代 ZGC 默认 │ Markdown 文档注释
       │
JDK 24 (2025.03) ─── Stream Gatherers │ Class-File API │ Quantum-Resistant Crypto

LTS 版本建议

  • 新项目: 推荐使用 JDK 21(最新 LTS,包含虚拟线程等重要特性)
  • 保守升级: 可选择 JDK 17(稳定的 LTS 版本)
  • 遗留系统: JDK 11 仍然是可靠选择
  • 不建议: JDK 8 已经较老,建议尽快升级

文档最后更新: 2025-01-07