Lesson 51 · Java 高级特性

Java 8 → 21 核心特性速览:var、record、sealed、虚拟线程

中级·#Java·#新特性

第 1 站

Java 半年一版本,哪些特性真正值得学?

面试官问:"说说你了解的 Java 8 之后的新特性。" 如果你只能答出 Lambda 和 Stream,说明你的知识还停在 2014 年。从 Java 9 开始,Oracle 把发布节奏改为每 6 个月一个版本——面对这么多版本,哪些特性值得放进生产环境?

2017 年 9 月,Oracle 宣布 Java 采用每 6 个月一个版本的新发布节奏,同时引入 LTS(长期支持)概念:只有 LTS 版本能获得长达数年的商业支持。到目前为止,关键的 LTS 版本有三个:

版本发布时间定位支持周期
Java 82014.03函数式编程元年Oracle 商业支持至 2030+
Java 112018.09首个新节奏 LTS支持至 2026+
Java 172021.09Spring Boot 3 最低要求支持至 2029+
Java 212023.09虚拟线程改变并发模型支持至 2031+

非 LTS 版本(如 9、10、12~16、18~20)中的特性会被"顺延"到下一个 LTS 中正式落地。因此我们按实际影响力和生产可用性来筛选,只讲面试和实战中真正高频的特性。

面试考法

面试官不期望你背出每个版本的所有 JEP 编号,而是想看你能否说出每个特性的动机、用法和取舍。能对比 record vs Lombok、虚拟线程 vs 协程的候选人,分数会明显更高。

第 2 站

Java 8(LTS):函数式编程的起点

Java 8 是整个 Java 生态的分水岭。它引入的函数式特性让 Java 从纯面向对象语言变成面向对象 + 函数式的混合范式。即使过了十年,这五个特性仍然是面试必考项。

Lambda + Stream + Optional 组合拳
// 1. Lambda:告别匿名内部类
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.sort((a, b) -> a.length() - b.length());

// 2. Stream:声明式数据处理管线
List<String> result = names.stream()
    .filter(n -> n.length() > 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 3. Optional:告别 NullPointerException
Optional<User> user = findById(42);
String name = user.map(User::getName).orElse("unknown");

Java 8 另外两个高频考点:

特性要点面试陷阱
Date/Time API
(java.time)
LocalDate、LocalTime、LocalDateTime 等不可变对象,线程安全,替代 Date/CalendarDate 是可变且非线程安全的,SimpleDateFormat 也是——这是经典坑
default 方法接口可以有默认实现,解决了接口演化问题菱形继承问题:两个接口都定义了同名 default 方法时,实现类必须显式覆写
要点

Java 8 的五大特性(Lambda、Stream、Optional、Date/Time、default 方法)是所有后续特性的基石。如果你对这些还不够熟练,应该先巩固它们再往后学。

第 3 站

Java 10-11:var 推断与 HTTP Client

Java 10 是非 LTS 版本,但它引入的 var 类型推断被保留到了所有后续版本中,成为开发者使用频率最高的语法糖之一。Java 11 作为 LTS 版本则带来了标准化的 HTTP Client。

var 局部变量类型推断
// Java 10+: 编译器自动推断类型
var list = new ArrayList<String>();      // 推断为 ArrayList<String>
var map  = new HashMap<String, Integer>(); // 推断为 HashMap<String, Integer>
var path = Paths.get("/tmp/data.csv");     // 推断为 Path

// ⚠️ var 只能用于局部变量,不能用于:
// - 方法参数、返回值
// - 类字段
// - 没有初始化器的变量
var 会不会降低代码可读性?答案是看场景。当右侧构造函数已经明确表达了类型(如 new ArrayList<User>()),用 var 反而减少噪音。但如果右侧是一个方法调用,如 var result = process(),你就丢失了类型信息——此时应该写明确类型。

Java 10 还引入了 List.of()Set.of()Map.of()不可变集合工厂方法,配合 var 使用尤其简洁:

不可变集合工厂 + var
// Java 9+ 不可变集合工厂方法
var colors = List.of("red", "green", "blue");
var config = Map.of(
    "timeout", 30,
    "retries", 3
);
// 返回的是不可变集合,调用 add() 会抛 UnsupportedOperationException
// 如果需要可变的,用 new ArrayList<>(List.of(...))

Java 11 的亮点特性:

HTTP Client 标准 API(JDK 11)
// 终于不用再依赖 Apache HttpClient 或 OkHttp 了
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Accept", "application/json")
    .GET()
    .build();

HttpResponse<String> response =
    client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());   // 200
System.out.println(response.body());          // JSON 字符串

Java 11 还给 String 加了几个实用方法:

方法用途示例
isBlank()判断是否为空或纯空白" ".isBlank() → true
strip()去除首尾空白(支持 Unicode 空白)trim() 更准确
lines()按行拆分为 Stream"a\nb\nc".lines().count() → 3
repeat(n)重复拼接 n 次"ha".repeat(3) → "hahaha"
第 4 站

Java 14-17(LTS):Record、Sealed、Pattern Matching

Java 17 是 Spring Boot 3 的最低版本要求,也是目前企业升级的主流目标。从 Java 14 到 17,四个特性经历了 preview → 正式发布的完整过程,设计质量非常高。

Record:不可变数据载体(Java 16 正式)
// 一行代码 = getter + equals + hashCode + toString + 不可变
public record Point(int x, int y) {}

// 编译器自动生成:
// - private final int x, y
// - 全参构造函数
// - x(), y() 访问器(注意:不是 getX())
// - equals / hashCode / toString

var p = new Point(3, 4);
System.out.println(p.x());           // 3
System.out.println(p);               // Point[x=3, y=4]

// 可以添加 compact constructor 做校验
public record User(String name, int age) {
    public User {
        if (age < 0) throw new IllegalArgumentException("age < 0");
    }
}
Record vs Lombok @Data

Record 是语言级特性(JVM 直接支持),Lombok 是编译期注解处理器。Record 保证不可变(字段 final),Lombok 的 @Data 生成可变类。如果你的 DTO 不需要 setter,Record 是更好的选择;如果需要 mutable bean,Lombok 仍然有优势。

Switch 表达式(Java 14 正式)
// 传统 switch:case 穿透、忘记 break 就出 bug
// 新 switch 表达式:有返回值,箭头语法无穿透
var label = switch (day) {
    case MONDAY, FRIDAY  -> "工作日";
    case TUESDAY         -> "周二";
    case SATURDAY, SUNDAY -> "周末";
    default              -> "其他";
};
// 不需要 break,不可能穿透,直接赋值给变量
Sealed 类(Java 17 正式)
// 限制哪些类可以继承——实现"代数数据类型"
public sealed interface Shape
    permits Circle, Rectangle, Triangle {}

public record Circle(double radius) implements Shape {}
public record Rectangle(double w, double h) implements Shape {}
public record Triangle(double base, double height) implements Shape {}

// sealed + record 组合:编译器知道所有子类型
// 为 Java 21 的 switch 模式匹配铺路

同期上线的其他实用特性:

特性版本一句话
Text BlocksJava 15""" 三引号多行字符串,写 SQL/JSON 不用再拼 +
instanceof 模式匹配Java 16if (obj instanceof String s) 直接拿到变量 s
Helpful NPEJava 14NPE 信息精确到"哪个字段是 null",不再只报行号
第 5 站

Java 21(LTS):虚拟线程改写并发规则

Java 21 是近十年来最重要的 Java 版本,没有之一。虚拟线程(Virtual Threads)从根本上改变了 Java 的并发编程模型,让"一个请求一个线程"的简单模型重新变得可行。

虚拟线程:百万级并发不是梦
// 传统方式:平台线程,每个线程 ~1MB 栈内存
// 10000 个线程 = 10GB 内存——这就是"线程很贵"的原因

// Java 21:虚拟线程,极其轻量
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 100_000).forEach(i ->
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        })
    );
}
// 10 万个虚拟线程,内存占用可能不到 100MB
// 因为虚拟线程不绑定 OS 线程,在阻塞时自动让出
Switch 模式匹配(Java 21 正式)
// sealed + record + switch 模式匹配 = 穷举检查
double area = switch (shape) {
    case Circle c    -> Math.PI * c.radius() * c.radius();
    case Rectangle r -> r.w() * r.h();
    case Triangle t  -> 0.5 * t.base() * t.height();
    // 无需 default!编译器知道 sealed 只有这三种
};

Java 21 的其他亮点:

特性状态说明
Sequenced Collections正式统一了 List、Deque、SortedSet 的顺序访问接口:getFirst()getLast()reversed()
Structured ConcurrencyPreview将多个并发任务视为一个单元,统一取消、统一异常处理
Scoped ValuesPreviewThreadLocal 的替代品,为虚拟线程优化,不可变且更安全
String TemplatesPreview类似 Kotlin 的字符串模板 STR."Hello \{name}"
要点

Java 21 最值得关注的特性排序:虚拟线程 > switch 模式匹配 > Sequenced Collections。虚拟线程是真正改变架构的特性,后两个是提升编码体验的语法糖。

第 6 站

虚拟线程深度解析:它是如何工作的?

面试高频追问:"虚拟线程底层是怎么实现的?和 Kotlin 协程有什么区别?为什么有人说虚拟线程是'协程的反方向'?" 能回答这些问题,说明你不只是会用 API,而是理解了 JVM 并发模型的演进方向。
虚拟线程调度模型 ForkJoinPool(Carrier Threads / 载体线程) Carrier Thread-1 Carrier Thread-2 Carrier Thread-3 ... N mount ▼ mount ▼ mount ▼ VThread-1 (running) VThread-5 (running) VThread-8 (running) unmount on I/O ▼ unmount on I/O ▼ 等待队列(Parked): VT-2 VT-3 VT-4 VT-6 VT-7 VT-9 ... 数万个 Carrier Thread(OS 线程,~1MB 栈) Virtual Thread(JVM 对象,~几KB) Parked(等待 I/O 完成)
图 1 虚拟线程通过 mount/unmount 机制在少量载体线程上复用,阻塞时不占用 OS 线程

虚拟线程的核心工作原理可以用三个关键词概括:

机制说明
Carrier Thread(载体线程)底层是一个 ForkJoinPool,线程数默认等于 CPU 核心数。虚拟线程并不直接跑在 OS 上,而是被调度到载体线程上执行
Mount(挂载)虚拟线程开始执行时,被"挂载"到一个载体线程上。此时它和普通线程一样运行
Unmount(卸载)当虚拟线程遇到阻塞操作(I/O、sleep、lock)时,JVM 自动将其从载体线程上"卸载",载体线程立刻去执行下一个虚拟线程。阻塞结束后,虚拟线程重新排队等待 mount
虚拟线程 vs 平台线程 vs 协程
// 平台线程(OS Thread)
// - 1:1 映射到 OS 线程
// - 栈内存 ~1MB,创建/切换开销大
// - 通常线程池限制在几百到几千

// 虚拟线程(Virtual Thread)—— Java 的方式
// - M:N 调度:M 个虚拟线程复用 N 个载体线程
// - 栈内存 ~几KB(存在 JVM 堆上,可被 GC)
// - 仍然是抢占式调度(不是协作式)

// 协程(Kotlin / Go)
// - 也是 M:N 调度,但使用协作式挂起(suspend)
// - 需要改写代码为 suspend/async 风格
// - Java 虚拟线程不需要改代码——所有阻塞自动处理
什么时候该用虚拟线程?

适合:I/O 密集型应用(HTTP 调用、数据库查询、文件读写)。虚拟线程在阻塞等待 I/O 时会自动让出载体线程,吞吐量可以提升数倍。
不适合:CPU 密集型计算。虚拟线程的载体线程池大小等于 CPU 核心数,纯计算场景下没有收益,还有调度开销。另外,synchronized 块和 native 方法会导致"pinned"(钉住)问题——载体线程无法被释放。此时应改用 ReentrantLock

虚拟线程最佳场景 = I/O 密集型 + 高并发 + 阻塞型编程风格
避免场景 = CPU 密集型 + synchronized 热点 + JNI native 调用
第 7 站

总结:特性采用优先级与时间线

面对这么多新特性,企业和个人应该按什么顺序升级?以下是一张优先级表:

优先级特性版本采用理由
P0Lambda + Stream8已成行业标准,不用就落后
P0Optional + Date/Time8消灭 NPE 和时间处理 bug
P1var 类型推断10减少样板代码,零运行时开销
P1Text Blocks15SQL、JSON、测试数据编写体验飞跃
P1Record16替代 Lombok 做 DTO/VO,更安全
P2Sealed Classes17配合 Record + Switch 实现穷举模式
P2Switch 模式匹配21替代 instanceof 链,编译器穷举检查
P0+虚拟线程21颠覆并发模型,I/O 密集场景必用
Java 核心特性发布时间线 Java 8 2014 Lambda Stream Optional 10 / 11 2018 var HttpClient 14 → 17 2020-21 Record Sealed Pattern Match Java 21 2023 Virtual Threads Switch Pattern Sequenced Coll. LTS LTS LTS LTS
图 2 Java 8 → 21 核心特性发布时间线(每 6 个月一个版本,LTS 版本标红)

全文回顾

  1. Java 8 是函数式编程的基础——Lambda、Stream、Optional 至今仍是使用频率最高的特性
  2. Java 10-11 带来了 var 和 HTTP Client,减少了样板代码
  3. Java 14-17 通过 Record、Sealed、Switch 表达式构建了"代数数据类型"三件套
  4. Java 21 的虚拟线程是近十年最重要的特性,让 Java 在 I/O 密集场景下重获简洁的同步编程模型
  5. 升级建议:先巩固 Java 8 → 升级 Java 17 用 Record + Sealed → 评估 Java 21 虚拟线程
面试锦囊

面试时不要罗列特性清单,而是围绕一个核心叙事来组织答案:Java 的演进方向是"减少样板代码 + 提升并发能力"。从 Lambda 到 Record 是在减少样板代码,从平台线程到虚拟线程是在提升并发能力。能把这两条线讲清楚,你的回答就有深度。