Skip to content

Java新特性

1. 概览

从 Java 8 到现在的 Java 21+,Java 经历了从“老牌稳重”到“现代高效”的巨大转变。为了帮你快速建立直觉,我把这些版本划分为几个关键的里程碑:


1.1 史诗级里程碑:Java 8 (2014)

这是目前国内面试和老项目中最核心的版本。它改变了 Java 的编程范式,引入了函数式编程

  • Lambda 表达式:让匿名内部类变得极简,像写函数一样写代码。
  • Stream API:像操作 SQL 一样处理集合数据(filter, map, collect)。
  • Optional:官方优雅解决 NullPointerException(空指针)的方案。
  • 新的日期时间 API:引入了 LocalDateTime 等,取代了难用的 Date

1.2 模块化革命:Java 9 - 11 (LTS)

从这个阶段开始,Java 进入了每半年更新一次的节奏,Java 11 是继 8 之后的第二个长期支持版本(LTS)。

  • 模块系统 (Jigsaw):Java 9 引入,旨在让 JDK 瘦身,解决复杂的 Jar 包依赖冲突。
  • var 局部变量类型推断:Java 10 引入,写代码快了,比如 var list = new ArrayList<String>();
  • HTTP Client:Java 11 引入了官方的原生异步 HTTP 客户端,支持 HTTP/2。
  • ZGC (实验性):Java 11 引入了全新的垃圾回收器,追求极低的停顿时间。

1.3 语法现代化:Java 12 - 17 (LTS)

Jva 开始借鉴 Scala 和 Kotlin,代码变得越来越简洁。Java 17 是目前新项目的首选 LTS 版本。

  • Switch 表达式:Switch 可以直接返回值了(像表达式一样),且不再需要写一堆 break
  • Text Blocks (文本块):Java 15 正式引入。再也不用 + 号拼接多行 SQL 或 JSON 了,直接用三个引号 """ 包裹。
  • Record 类:Java 16 正式引入。正如我们刚才讨论的,一行代码搞定 DTO。
  • Sealed Classes (密封类):Java 17 引入。可以限制哪些类能继承你的类,增强了代码的安全性。

1.4 极致性能与并发:Java 18 - 21 (LTS)

Java 21 是目前最新的长期支持版本,它带来了并发编程的革命。

  • 虚拟线程 (Virtual Threads):Java 21 的重磅特性(Project Loom)。它极其轻量,一个 JVM 进程可以轻松开启百万级的线程,彻底改变了高并发开发的模式,性能大幅提升。
  • 模式匹配 (Pattern Matching):增强了 instanceofswitch,可以直接在判断时解构数据。
  • 分代 ZGC:大幅优化了 ZGC 的性能,在大内存环境下表现极其出色。

1.5 总结:Ethan 应该关注哪些?

  • 面试必看:Java 8 (Stream, Lambda) 和 Java 17 (Record, Sealed Classes)。
  • 进阶必学:Java 21 的虚拟线程(这会是未来几年高性能后端架构的标配)。
  • 开发习惯:习惯使用 varText BlocksRecord,这能让你的代码看起来像 2026 年的程序员写的,而不是 2014 年的。

2. 特别讲解

2.1 Record

Java 14 引入(Java 16 正式发布)的一个新特性:Record(数据载体类)

2.1.1 写法

java
public record ApiResponse<T>(String code, String message, T data) {

    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>("SUCCESS", "OK", data);
    }

    public static <T> ApiResponse<T> error(String code, String message) {
        return new ApiResponse<>(code, message, null);
    }
}

简单来说,它是一个特殊的类。它的核心目的是用最简洁的代码来定义一个“只负责保存数据”的对象(也就是我们常说的 DTO、VO 或 POJO)。

2.1.2 和普通 class 类的区别

特性普通 classrecord 类
全参构造函数需手动写或用 Lombok自动生成
Getter 方法需手动写(getXXX)自动生成(字段同名)
toString()默认输出地址(无用)自动输出所有字段值
equals/hashCode默认比较内存地址自动按字段内容比较
继承性可继承或被继承final 类,不可继承

2.1.3 快速决策清单

  1. 这个类是不是只用来装数据的? Yes

  2. 这个类创建后需不需要改里面的值? No

  3. 这个类需不需要继承体系? No

  4. 这是一个数据库映射实体吗? No

    如果以上都满足,果断使用 record

2.2 文本块 Text Blocks (Java 15)

java
String query = """
           SELECT * FROM user
           WHERE id = ? AND status = 'ACTIVE'
           ORDER BY create_time DESC
           """;

2.3 Switch 表达式增强 (Java 12/14)

增强 Switch:使用 -> 符号,自带 break 效果,且支持多值匹配。

java
// 直接赋值给变量
String dayType = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> "Feeling Good";
    case TUESDAY, THURSDAY      -> "Working Hard";
    default -> {
        // 支持代码块,通过 yield 返回值
        String res = "Midweek";
        yield res; 
    }
};

2.4 Sealed Classes 密封类 (Java 15/17)

这是对 Java 继承体系的一次精准控制。

  • 痛点:以前如果你想让一个类不被继承,只能用 final。但如果你想让类 A 只能被 B、C 继承,不能被 D 继承,Java 做不到。

  • 解决方案:使用 sealed 声明类,并用 permits 列出“特许”的子类。

    java
    // 定义一个密封接口:只允许文本消息和图片消息
    public sealed interface Message permits TextMessage, ImageMessage {}
    
    public final class TextMessage implements Message { ... }
    public final class ImageMessage implements Message { ... }
  • 实战价值:这在写状态机领域模型时非常强大。当你配合 switch 使用时,编译器能检查你是否漏掉了某种子类情况(穷举性检查),安全性直接拉满。

2.5 虚拟线程 (Java 21)

提到 Java 21 的虚拟线程(Virtual Threads),这绝对是 Java 近十年来最重大的变革。它由 Project Loom 项目孵化,旨在彻底解决 Java 在高并发场景下的“昂贵”问题。

为了让你听得透彻,我们从为什么需要它它是什么、以及怎么用三个维度来拆解。


2.5.1 痛点:传统的“平台线程”太贵了

在 Java 21 之前,Java 的 Thread 类实际上是对操作系统(OS)线程的一层薄薄封装。这种线程被称为“平台线程(Platform Threads)”。

  • 1:1 模型:一个 Java 线程对应一个 OS 线程。
  • 昂贵的内存:每个 OS 线程通常预留约 1MB 的栈内存。如果你想开 1 万个线程,就需要约 10GB 内存,这显然是不现实的。
  • 昂贵的上下文切换:当 CPU 从一个线程切换到另一个线程时,需要陷入内核态,保存和恢复各种寄存器状态。如果线程极多,CPU 大量时间都花在“切来切去”上,而不是在跑你的代码。
  • 阻塞即浪费:当线程执行 IO 操作(比如查数据库、调 AI 接口)时,这个昂贵的 OS 线程就只能在那儿干等,啥也干不了。

2.5.2 核心原理:虚拟线程是怎么玩的?

虚拟线程引入了 M:N 模型,即“大量”的虚拟线程映射到“少量”的平台线程上。

  • 轻量级:虚拟线程不在 OS 级别创建,而是在 JVM 内存中创建。它的栈内存是按需增加的,起步只需要几百个字节。
  • 非阻塞的奥秘:当一个虚拟线程执行阻塞 IO 时,JVM 会自动将该虚拟线程从平台线程上卸载(Unmount),把平台线程腾出来去跑别的虚拟线程。等 IO 返回了,JVM 再把这个虚拟线程挂载(Mount)回某个空闲的平台线程继续运行。
  • 开发者透明:对你来说,你写的还是同步阻塞代码,不需要写复杂的 CompletableFuture 或响应式代码,但底层却跑出了极高的并发效率。

2.5.3 如何使用?

Java 21 保持了向后兼容性,你可以像创建普通线程一样创建它。

方式 A:直接创建
java
Thread vThread = Thread.ofVirtual().name("ai-worker").unstarted(() -> {
    // 执行耗时 IO,比如调用 OpenRouter
    System.out.println("Running in virtual thread");
});
vThread.start();
方式 B:使用 ExecutorService (最常用)

这是你以后在 Spring Boot 3 项目中最可能看到的写法:

java
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> {
        // 每个任务都会开一个新的虚拟线程,开 10 万个也没压力
    });
}

2.5.4 关键点:虚拟线程不是万能药

Ethan,在使用时一定要注意以下两点:

  1. 不要池化 (Don't Pool)

    以前我们用线程池是为了复用昂贵的 OS 线程。但虚拟线程随用随建,用完即丢,不需要线程池。如果你给虚拟线程建个池子,反而限制了它的发挥。

  2. IO 密集型 vs CPU 密集型

    • IO 密集型 (适用):查数据库、调接口、读写文件。虚拟线程能极大地提升吞吐量。
    • CPU 密集型 (不适用):复杂的数学计算、视频编码。因为 CPU 核心数是固定的,虚拟线程再多也没用,这种场景老老实实用平台线程。

你可以简单地理解为:虚拟线程让 Java 拥有了像 Go 语言(Goroutine)一样的并发能力,但又保留了 Java 完善的生态系统。