从“Oak”到“虚拟线程”:JDK 1.0到25演进全记录与核心知识点详解a

引言

如果你是Java开发者,一定听过"一次编写,到处运行"这句口号。但你可能不知道,从1996年第一个正式版本到今天,Java已经走过了近三十年的演进历程。这期间,它经历了互联网泡沫、.NET的挑战、Oracle收购、模块化转型,以及云原生时代的重生。

JDK的每个版本都承载着那个时代的印记:1.2让Java真正走向企业级,5.0带来了语法革命,8开启了函数式编程,11完成了模块化瘦身,21则用虚拟线程重构了并发编程模型。

今天,我们顺着时间线,看看JDK从1.0到25是如何一步步演变成今天这个样子的。每个版本的核心特性、设计思想、实用知识点,以及当年开发者踩过的坑,我都会一一梳理清楚。

第一章:JDK 1.0到1.4 ------ 从诞生到企业级奠基(1996-2002)

1.1 JDK 1.0(1996):Oak的诞生,Java的起点

历史背景:Java最初由Sun公司的"Green"项目组开发,用于智能家电设备,最初名为Oak。因商标被占用,1995年更名为"Java"------灵感来自开发人员常去的一家名叫"Java Coffee"的咖啡店。

1996年1月23日,JDK 1.0正式发布。这是一个纯解释运行的版本,使用外挂JIT,性能比较差。

核心特性

  • Java语言本身:类、接口、异常处理等基础语法

  • 基础类库 :java.lang、java.io、java.util等

  • Applet支持:能在浏览器中运行的小程序,当时最吸引人的特性

  • AWT:Abstract Window Toolkit,图形用户界面工具包

实用知识点:Applet在当时被誉为"Web的未来",但后来因为安全性问题和Flash的崛起逐渐衰落。如果你维护过2000年左右的老系统,可能还会看到Applet代码。

1.2 JDK 1.1(1997):内部类与JDBC

1997年2月,JDK 1.1发布。

核心特性

  • 内部类:可以在类内部定义类,为事件处理等场景提供便利

  • JavaBeans:可重用的组件模型

  • JDBC:Java Database Connectivity,数据库连接API,让Java可以操作关系数据库

  • RMI:Remote Method Invocation,远程方法调用,支持分布式计算

  • 反射API:可以在运行时获取类的信息、调用方法

实用知识点:内部类直到今天仍是常用特性,尤其在事件监听和集合操作中。但要注意,内部类会持有外部类的引用,容易引发内存泄漏。

1.3 JDK 1.2(1998):Java 2时代,集合框架诞生

1998年12月8日 ,JDK 1.2发布,Sun将其更名为Java 2,并分为三个平台:J2SE(标准版)、J2EE(企业版)、J2ME(微型版)。

核心特性

  • 集合框架:List、Set、Map等接口和实现类,取代了之前的Vector、Hashtable

  • JIT编译器:即时编译,大幅提升性能

  • Swing:轻量级GUI组件库,比AWT更丰富、更美观

  • Java2D:二维图形API

  • 数字签名:对打包的Java文件进行数字签名

  • Java插件:浏览器中运行Applet的插件

虚拟机并存:这个阶段并存过三个虚拟机------Classic VM、Exact VM和HotSpot VM。HotSpot于1999年4月诞生,后来成为主流。

实用知识点:集合框架是1.2最重要的贡献,它统一了Java的数据结构API。直到今天,我们每天都在用ArrayList、HashMap。

1.4 JDK 1.3(2000):性能优化与HotSpot普及

2000年5月8日,JDK 1.3发布。

核心特性

  • HotSpot VM成为默认:提供更优的性能和内存管理

  • Java Sound API:音频处理

  • JNDI:Java Naming and Directory Interface,命名和目录服务访问

  • jar文件索引:优化类加载性能

历史背景:当时Sun正面临来自Microsoft的严峻挑战,后者推出了.NET框架试图颠覆Java的地位。Sun决定加快Java的开发速度以增强竞争力。

1.5 JDK 1.4(2002):第一个LTS,NIO与断言

2002年2月13日,JDK 1.4发布。这是Java SE中功能最全面的版本之一,也是第一个获得长期支持(LTS)的版本。

核心特性

  • NIO:New I/O,非阻塞I/O API,为高性能网络编程奠基

  • 断言:assert关键字,用于调试

  • 正则表达式:java.util.regex包

  • 日志API:java.util.logging

  • XML处理:内置SAX和DOM解析器

  • 链式异常:异常可以包装另一个异常

  • IPV6支持

实用知识点:NIO的出现让Java真正具备了开发高性能网络服务器的能力。后来的Netty、Tomcat NIO组件都基于此。

版本 年份 核心主题 代表特性
1.0 1996 诞生 Applet、AWT、基础类库
1.1 1997 数据库连接 内部类、JDBC、RMI、反射
1.2 1998 Java 2时代 集合框架、Swing、JIT
1.3 2000 性能优化 HotSpot VM、Java Sound
1.4 2002 企业级能力 NIO、正则、断言、日志

第二章:JDK 5和6 ------ 语法革命与生态繁荣(2004-2006)

2.1 JDK 5(2004):史上最大语法更新

2004年9月30日,JDK 5发布。这是继1.0之后最重大的版本更新,Sun放弃了"JDK 1.x"的命名方式,直接称为JDK 5。

为什么改名? Sun想传达Java平台快速迭代的理念,避免版本号混淆。

核心特性

特性 作用 示例
泛型 类型安全的集合 List<String> list = new ArrayList<String>();
增强for循环 简化遍历 for (String s : list) { ... }
自动装箱/拆箱 基本类型与包装类自动转换 Integer i = 100;
枚举 类型安全的常量 enum Color { RED, GREEN, BLUE }
可变参数 方法接收任意数量参数 void print(String... args)
静态导入 直接使用静态成员 import static java.lang.Math.*;
注解 元数据支持 @Override@Deprecated
并发包 java.util.concurrent 线程池、锁、并发集合

代码对比

java

复制代码
// JDK 1.4风格
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

// JDK 5风格
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);  // 无需强制转型

实用知识点 :泛型的本质是类型擦除,运行时List<String>和List<Integer>是同一个类。这导致了一些限制,比如不能new T()、不能instanceof List<String>

2.2 JDK 6(2006):性能优化与脚本支持

2006年12月11日,JDK 6发布。

核心特性

  • JDBC 4.0:简化的数据库操作,自动加载驱动

  • JConsole:Java监视与管理控制台

  • 脚本语言支持:通过JSR 223集成JavaScript等脚本语言

  • Compiler API:可以在程序中调用Java编译器

  • Web Services:内置对XML、SOAP的支持

  • 轻量级HTTP服务器:用于测试

历史背景:JDK 6的生命周期异常长,直到2014年JDK 8发布前,它都是企业级应用的主流选择。即使在今天,一些银行系统仍在运行JDK 6。

为什么银行还坚守JDK 6?

  • 稳定性高:经过多年验证,符合金融行业对稳定性的严苛要求

  • 可控性强:新特性较少,容易维护和掌控

  • 成本考虑:升级需要修改代码、重新测试、重新审计,成本巨大

2009年,Oracle以74亿美元收购Sun公司。Java正式归属Oracle,一个新时代开始了。

第三章:JDK 7和8 ------ Oracle时代与函数式革命(2011-2014)

3.1 JDK 7(2011):曲折的过渡版本

2011年7月28日,JDK 7发布。

历史背景:在JDK 7开发期间,Sun公司陷入财务困境,股票市值跌至高峰时期的3%,无力按计划推进研发。Oracle收购后立即实行"B计划",大幅裁剪预定目标,才保证了准时发布。

核心特性

  • switch支持字符串

    java

    复制代码
    switch (str) {
        case "hello": break;
        case "world": break;
    }
  • 菱形操作符:泛型类型推断

    java

    复制代码
    List<String> list = new ArrayList<>();  // 无需重复写类型
  • try-with-resources:自动关闭资源

    java

    复制代码
    try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
        // 使用完后自动关闭
    }
  • 多异常捕获

    java

    复制代码
    try {
        // ...
    } catch (IOException | SQLException e) {
        // 统一处理
    }
  • 二进制字面量:0b开头表示二进制

    java

    复制代码
    int binary = 0b1010;  // 十进制10
  • 数字字面量加下划线:提高可读性

    java

    复制代码
    int million = 1_000_000;
  • NIO.2:增强的文件操作API

    java

    复制代码
    Path path = Paths.get("/tmp/file.txt");
    Files.copy(path, Paths.get("/tmp/copy.txt"));

3.2 JDK 8(2014):函数式编程的革命

2014年3月18日,JDK 8发布。这是继JDK 5之后最重要的版本,也是目前最流行的LTS版本。

核心特性

3.2.1 Lambda表达式

这是JDK 8最核心的特性,让Java具备了函数式编程能力。

java

复制代码
// 传统方式
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

// Lambda方式
Collections.sort(list, (a, b) -> a.length() - b.length());

// 更简洁的方法引用
Collections.sort(list, Comparator.comparingInt(String::length));

Lambda语法

  • (参数) -> 表达式

  • (参数) -> { 语句; }

3.2.2 Stream API

Stream提供了一种声明式处理数据集合的方式。

java

复制代码
List<User> users = Arrays.asList(new User("张三", 20), new User("李四", 30), ...);

// 筛选年龄>18的用户,提取姓名,排序,收集到列表
List<String> names = users.stream()
    .filter(u -> u.getAge() > 18)
    .map(User::getName)
    .sorted()
    .collect(Collectors.toList());

// 并行流
users.parallelStream()
    .filter(u -> u.getAge() > 18)
    .forEach(System.out::println);

Stream操作分类

  • 中间操作:filter、map、sorted、distinct、limit

  • 终端操作:forEach、collect、count、reduce、anyMatch

3.2.3 新日期时间API

彻底重写了日期时间库,解决了旧版Date的可变性和线程安全问题。

java

复制代码
// 创建日期
LocalDate date = LocalDate.of(2026, 2, 18);
LocalTime time = LocalTime.of(10, 30);
LocalDateTime datetime = LocalDateTime.now();

// 日期运算
LocalDate tomorrow = date.plusDays(1);
LocalDate lastMonth = date.minusMonths(1);

// 格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formatted = date.format(formatter);

// 时区处理
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
3.2.4 接口默认方法与静态方法

java

复制代码
public interface MyInterface {
    // 抽象方法
    void abstractMethod();
    
    // 默认方法:有实现的方法
    default void defaultMethod() {
        System.out.println("默认实现");
    }
    
    // 静态方法
    static void staticMethod() {
        System.out.println("静态方法");
    }
}
3.2.5 Optional类

用于优雅地处理空指针问题。

java

复制代码
// 传统方式
public String getCity(User user) {
    if (user != null && user.getAddress() != null) {
        return user.getAddress().getCity();
    }
    return "未知";
}

// Optional方式
public String getCity(User user) {
    return Optional.ofNullable(user)
        .map(User::getAddress)
        .map(Address::getCity)
        .orElse("未知");
}

其他特性

  • CompletableFuture:增强的异步编程

  • Nashorn JavaScript引擎:在JVM上运行JavaScript

  • 重复注解:同一个位置可以多次使用同一注解

  • 方法引用System.out::println语法

JDK 8为什么能统治十年?

  • Lambda和Stream让代码量减少40%,函数式编程简化了集合操作

  • Optional将空指针异常关进笼子

  • 新日期API解决了长期以来的痛点

  • 性能优化和生态成熟度达到顶峰

第四章:JDK 9到11 ------ 模块化转型与发布周期变革(2017-2018)

4.1 发布周期革命:从大版本到半年一发

从JDK 9开始,Oracle改变了发布策略:

  • 每6个月发布一个功能版本(3月和9月)

  • 每3年(后来改为2年)发布一个LTS版本

  • LTS版本获得长期支持和更新

这意味着:

  • JDK 8之后,下一个LTS是JDK 11

  • 非LTS版本只有6个月的支持期,不建议在生产环境使用

4.2 JDK 9(2017):模块化系统(Jigsaw)

2017年9月22日,JDK 9发布。

核心特性

4.2.1 模块系统(JPMS)

这是JDK 9最大的变化,将JDK自身也模块化了。

模块描述文件module-info.java

java

复制代码
module com.example.myapp {
    requires java.sql;                 // 依赖其他模块
    exports com.example.myapp.api;      // 导出包供其他模块使用
    uses com.example.myapp.spi.Service;  // 声明使用服务
    provides com.example.myapp.spi.Service 
        with com.example.myapp.impl.ServiceImpl;  // 提供服务实现
}

模块化的好处

  • 强封装:可以控制哪些包对外可见

  • 可靠配置:避免ClassPath上的Jar冲突

  • 可伸缩:可以构建更小的运行时镜像(通过jlink)

4.2.2 JShell

交互式编程环境,可以逐行执行Java代码,类似Python的REPL。

bash

复制代码
$ jshell
jshell> int a = 10
a ==> 10
jshell> System.out.println(a * 2)
20
4.2.3 其他特性
  • HTTP/2客户端(孵化阶段):新的HTTP客户端API

  • 集合工厂方法:快速创建不可变集合

    java

    复制代码
    List.of("a", "b", "c");
    Set.of("x", "y", "z");
    Map.of("k1", "v1", "k2", "v2");
  • 私有接口方法:接口中可以有private方法

  • 改进的try-with-resources:可以使用effectively final变量

  • 响应式流API:java.util.concurrent.Flow

4.3 JDK 10(2018):局部变量类型推断

2018年3月20日,JDK 10发布。

核心特性

4.3.1 var:局部变量类型推断

java

复制代码
// 之前
List<String> list = new ArrayList<>();
Map<String, List<Integer>> map = new HashMap<>();

// 之后
var list = new ArrayList<String>();
var map = new HashMap<String, List<Integer>>();

// 循环中
for (var entry : map.entrySet()) {
    var key = entry.getKey();
    var value = entry.getValue();
}

使用限制

  • 只能用于局部变量(方法内)

  • 必须在声明时初始化

  • 不能用于方法参数、返回值、字段

4.3.2 其他特性
  • G1并行Full GC:优化G1垃圾收集器

  • 应用类数据共享:AppCDS,减少启动时间

  • 根证书:OpenJDK中提供一组默认根证书

4.4 JDK 11(2018):第二个LTS,企业升级首选

2018年9月25日,JDK 11发布。这是继JDK 8之后的第二个LTS版本,目前仍有约48%的市场份额。

核心特性

4.4.1 HTTP Client标准化

JDK 9孵化的HTTP Client在JDK 11中正式标准化。

java

复制代码
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/users"))
        .build();

// 同步请求
HttpResponse<String> response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

// 异步请求
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
    .thenApply(HttpResponse::body)
    .thenAccept(System.out::println);
4.4.2 简化启动单个文件

bash

复制代码
# 无需编译,直接运行
java HelloWorld.java
4.4.3 字符串增强

java

复制代码
"  hello  ".isBlank();        // true/false
"hello".repeat(3);             // "hellohellohello"
"  hello  ".strip();           // "hello" (去除全角和半角空格)
"a,b,c".lines().count();       // 3
4.4.4 集合转数组

java

复制代码
// 之前
String[] arr = list.toArray(new String[0]);

// 之后
String[] arr = list.toArray(String[]::new);
4.4.5 垃圾收集器
  • ZGC:可伸缩的低延迟垃圾收集器(实验性)

  • Epsilon GC:无操作的垃圾收集器,用于性能测试

被移除的特性

  • Java EE模块(JAXB、JAX-WS等)被移除,需要单独引入

  • JavaFX被分离为独立模块

  • Nashorn JavaScript引擎被废弃

JDK 11的地位:作为JDK 8之后第一个LTS,它是企业升级的"安全垫"------语法比8更简洁,模块化让应用更轻量,同时避开了9/10的过渡期不稳定性。

第五章:JDK 12到17 ------ 模式匹配与云原生时代(2019-2021)

5.1 预览特性机制

从JDK 12开始,Java引入了预览特性(Preview Features)机制:一些新特性先以预览形式发布,让开发者试用反馈,经过1-2个版本后正式转正。这既加快了创新速度,又保证了稳定性。

5.2 JDK 12-16:预览特性轮番登场

JDK 12(2019.3)
  • Switch表达式(预览):可以作为表达式返回值

    java

    复制代码
    int numLetters = switch (day) {
        case MONDAY, FRIDAY, SUNDAY -> 6;
        case TUESDAY                -> 7;
        case THURSDAY, SATURDAY     -> 8;
        case WEDNESDAY              -> 9;
    };
  • Shenandoah GC:低暂停时间垃圾收集器(实验性)

JDK 13(2019.9)
  • 文本块(预览):多行字符串

    java

    复制代码
    String json = """
        {
            "name": "张三",
            "age": 25
        }
        """;
  • Switch表达式增强(预览)

JDK 14(2020.3)
  • instanceof模式匹配(预览)

    java

    复制代码
    // 之前
    if (obj instanceof String) {
        String s = (String) obj;
        System.out.println(s.length());
    }
    
    // 之后
    if (obj instanceof String s) {
        System.out.println(s.length());  // 直接使用s
    }
  • Records(预览):透明数据载体

    java

    复制代码
    record Point(int x, int y) {}
  • 文本块转正

JDK 15(2020.9)
  • 密封类(预览):限制哪些类可以继承

    java

    复制代码
    sealed interface Shape 
        permits Circle, Rectangle, Triangle {
    }
  • Records(第二次预览)

  • instanceof模式匹配(第二次预览)

JDK 16(2021.3)
  • Records转正

    java

    复制代码
    // 一行搞定数据类
    record User(Long id, String name, String email) {}
    
    // 使用
    User user = new User(1L, "张三", "zhang@example.com");
    System.out.println(user.name());  // 自动生成getter
  • instanceof模式匹配转正

  • 密封类(第二次预览)

5.3 JDK 17(2021.9):第三个LTS,现代Java的标配

2021年9月14日,JDK 17发布。这是继8和11之后的第三个LTS版本,目前市场份额约45%。Spring Boot 3.x要求JDK 17+,使其成为新项目的标配。

核心特性

5.3.1 密封类转正

java

复制代码
public sealed class Shape
    permits Circle, Rectangle, Triangle {
}

final class Circle extends Shape { /* ... */ }
final class Rectangle extends Shape { /* ... */ }
final class Triangle extends Shape { /* ... */ }
// 其他类不能继承Shape
5.3.2 增强的伪随机数生成器

java

复制代码
RandomGeneratorFactory.all()
    .map(f -> f.name())
    .sorted()
    .forEach(System.out::println);

RandomGenerator random = RandomGeneratorFactory
    .of("L128X256MixRandom")
    .create();
5.3.3 上下文特定的反序列化过滤器

增强反序列化安全性,可针对特定上下文设置过滤器。

5.3.4 移除和废弃
  • Applet API被标记为废弃

  • RMI激活被废弃

  • 安全管理器被标记为废弃

为什么JDK 17是新项目标配?

  • 语法减负:文本块、Records、instanceof模式匹配让代码更简洁

  • 性能定型:ZGC和Shenandoah GC经过多个版本验证,稳定可靠

  • 生态适配:Spring Boot 3.x、Quarkus等主流框架都基于JDK 17+

  • 默认UTF-8:解决跨平台编码问题

第六章:JDK 18到21 ------ 虚拟线程革命(2022-2023)

6.1 JDK 18-20:为虚拟线程铺路

JDK 18(2022.3)
  • 简单Web服务器:命令行启动简易HTTP服务器

  • UTF-8默认编码:解决跨平台编码问题

  • 简单的API文档片段:支持在javadoc中加入代码片段

JDK 19(2022.9)
  • 虚拟线程(预览):轻量级线程,JDK并发编程的革命

  • 结构化并发(孵化):简化多线程编程

  • 向量API(孵化):SIMD指令支持

JDK 20(2023.3)
  • 虚拟线程(第二次预览)

  • 结构化并发(第二次孵化)

  • 作用域值(孵化):替代ThreadLocal的线程间共享数据方案

  • 记录模式(预览):在模式匹配中解构Records

6.2 JDK 21(2023.9):第四个LTS,虚拟线程正式版

2023年9月19日,JDK 21发布。这是继17之后的第四个LTS版本,被普遍认为是继Java 8之后的下一个主流版本。

核心特性

6.2.1 虚拟线程正式发布

这是JDK 21最重磅的特性,彻底改变了Java的并发编程模型。

什么是虚拟线程?

  • 传统的Java线程是操作系统线程(平台线程),创建成本高、内存占用大(约1MB)

  • 虚拟线程由JVM管理,是JDK级别的轻量级线程,创建成本极低、内存占用小(几KB)

  • 单个JVM可以创建数百万个虚拟线程

传统线程模型的问题

java

复制代码
// 每个请求占用一个线程
ExecutorService executor = Executors.newFixedThreadPool(200);
for (int i = 0; i < 10000; i++) {
    executor.submit(() -> {
        // 阻塞调用会占用线程
        String result = httpClient.get("https://api.example.com");
        return result;
    });
}
// 如果并发超过200,请求会排队

虚拟线程的写法

java

复制代码
// 无需线程池,每个任务一个虚拟线程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10000; i++) {
        executor.submit(() -> {
            // 阻塞调用不会阻塞底层操作系统线程
            String result = httpClient.get("https://api.example.com");
            return result;
        });
    }
}

工作原理

  1. 虚拟线程运行在称为"载体线程"的平台线程上

  2. 当虚拟线程执行阻塞操作(I/O、锁等)时,会自动从载体线程卸载

  3. 载体线程可以去执行其他虚拟线程

  4. 阻塞操作完成后,虚拟线程重新调度到某个载体线程上

性能对比(4C8G机器):

指标 平台线程池 虚拟线程 提升
最大并发 200 10000+ 50x
创建10000线程耗时 3秒+ <100ms 30x+
P99延迟 高(排队) 低(无阻塞) 大幅优化

使用建议

  • I/O密集型任务:虚拟线程优势明显

  • CPU密集型任务:使用平台线程,与CPU核心数匹配

  • 避免synchronized:会钉住载体线程,改用ReentrantLock

  • 慎用ThreadLocal:虚拟线程数量大,推荐使用ScopedValue

6.2.2 结构化并发

将相关任务看作一个整体单元,简化错误处理和取消。

java

复制代码
// 传统方式:需要手动管理多个线程的异常和取消
Future<String> user = executor.submit(() -> fetchUser());
Future<Integer> order = executor.submit(() -> fetchOrder());
try {
    String u = user.get();  // 可能抛出异常
    Integer o = order.get();
} catch (Exception e) {
    user.cancel(true);
    order.cancel(true);
    throw e;
}

// 结构化并发
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future<String> user = scope.fork(() -> fetchUser());
    Future<Integer> order = scope.fork(() -> fetchOrder());
    
    scope.join();          // 等待所有任务
    scope.throwIfFailed(); // 如果有任务失败,抛出异常
    
    return new Result(user.resultNow(), order.resultNow());
} // 自动取消未完成的任务
6.2.3 作用域值

替代ThreadLocal的更高效的线程间共享数据方案。

java

复制代码
// 定义作用域值
private static final ScopedValue<User> LOGGED_IN_USER = ScopedValue.newInstance();

// 绑定值并在作用域内运行
ScopedValue.where(LOGGED_IN_USER, user)
    .run(() -> {
        // 在这个作用域内可以访问LOGGED_IN_USER
        processRequest();
    });

// 在方法中获取
void processRequest() {
    User user = LOGGED_IN_USER.get();
    // ...
}
6.2.4 记录模式

可以在模式匹配中解构Records。

java

复制代码
record Point(int x, int y) {}
record Line(Point start, Point end) {}

// 之前
if (obj instanceof Line l) {
    Point s = l.start();
    Point e = l.end();
    System.out.println(s.x() + "," + s.y());
}

// 记录模式
if (obj instanceof Line(Point(var x1, var y1), Point(var x2, var y2))) {
    System.out.println(x1 + "," + y1);
}
6.2.5 Switch模式匹配

java

复制代码
// 之前
String formatted;
if (obj instanceof Integer i) {
    formatted = String.format("int %d", i);
} else if (obj instanceof Long l) {
    formatted = String.format("long %d", l);
} else if (obj instanceof String s) {
    formatted = String.format("String %s", s);
} else {
    formatted = "unknown";
}

// 之后
String formatted = switch (obj) {
    case Integer i -> String.format("int %d", i);
    case Long l    -> String.format("long %d", l);
    case String s  -> String.format("String %s", s);
    default        -> "unknown";
};

其他特性

  • 序列化集合 :有序的LinkedHashMapLinkedHashSet性能优化

  • 分代ZGC:降低垃圾收集开销

  • Key Encapsulation Mechanism API:KEM加密支持

第七章:JDK 22到25 ------ AI与云原生深化(2024-2026)

7.1 JDK 22(2024.3)

核心特性

  • 未命名变量和模式 :用_表示不使用的变量

    java

    复制代码
    try (var _ = conn.prepareStatement(sql)) { ... }
    
    if (obj instanceof Point(int x, int _)) { ... }
  • 外部函数与内存API:安全高效地调用本地代码

  • 字符串模板(第二次预览)

    java

    复制代码
    String name = "张三";
    String info = STR."用户:\{name}";

7.2 JDK 23(2024.9)

核心特性

  • 矢量API(第八次孵化):SIMD指令支持,为AI计算加速

  • 作用域值(第三次预览)

  • 隐式声明的类和实例主方法:简化入门

7.3 JDK 24(2025.3)

核心特性

  • 类文件API:解析和生成Java类文件

  • 紧凑对象头:减少内存占用(为JDK 25铺路)

  • 量子安全加密API:PEM格式支持

7.4 JDK 25(2025.9):第五个LTS,AI时代的Java

2025年9月,JDK 25发布。这是第五个LTS版本,支持周期至2033年。

核心特性

7.4.1 紧凑对象头正式版
  • 对象头从96-128位压缩到64位

  • 堆内存占用减少20%,适合云原生环境

7.4.2 Float16向量运算
  • 支持半精度浮点数运算

  • AI推理效率提升25%,适合机器学习计算

7.4.3 AOT编译优化
  • AOT编译启动速度提升30%+

  • 原生镜像更小、更快

7.4.4 PEM加密API正式版
  • 支持量子安全的PEM格式密钥

  • 适配后量子密码学标准

JDK 25的定位

  • 面向AI推理引擎、数值计算项目

  • 适合云原生服务(追求低内存、快启动)

  • 创新型团队技术预研的首选

第八章:版本演进全景图与选型指南

8.1 LTS版本一览

版本 发布时间 支持截止 核心主题 代表特性
JDK 8 2014.3 2030.12(商业) 函数式革命 Lambda、Stream、Optional
JDK 11 2018.9 2026.9(社区) 模块化转型 HTTP Client、ZGC孵化、var
JDK 17 2021.9 2029.9 语法完善 Records、密封类、模式匹配
JDK 21 2023.9 2031.9 并发革命 虚拟线程、结构化并发
JDK 25 2025.9 2033.9 AI原生 紧凑对象头、Float16向量

8.2 非LTS版本定位

非LTS版本(如JDK 9、10、12-16、18-20、22-24)是"功能预览版":

  • 每6个月发布,只有6个月支持期

  • 引入预览特性,供开发者试用反馈

  • 不建议在生产环境使用,除非有特定需求

8.3 版本选型建议

根据2026年初的实际情况:

场景 推荐版本 理由
遗留系统维护 JDK 8 金融、政务等老旧系统,升级成本高,商业支持至2030年
企业过渡升级 JDK 17 从8/11迁移,稳定性高,生态完善,Spring Boot 3.x适配
新项目标杆 JDK 21 虚拟线程大幅简化并发编程,支持周期至2031年
前沿技术布局 JDK 25 AI计算优化、内存占用低、支持至2033年,适合云原生

8.4 升级避坑指南

从8到11
  • 移除Java EE模块:JAXB、JAX-WS等需要单独引入

  • ClassLoader变化:有些应用依赖的类加载行为改变

  • 废弃APIfinalize()Thread.destroy()等被移除

从11到17
  • 封装JDK内部API :使用--add-opens或改用公共API

  • 预览特性:如果用了预览特性(如Records预览版),需要调整

  • 默认编码UTF-8:依赖系统编码的应用需注意

从17到21
  • 虚拟线程适配 :检查synchronizedThreadLocal使用

  • 新反射限制:部分私有字段访问受限

  • 预览特性升级:如Switch模式匹配语法可能有微调

第九章:面试题库

5道难度递增的基础面试题

第1题:JDK、JRE和JVM有什么区别?

难度:⭐

参考答案

组件 全称 作用
JDK Java Development Kit Java开发工具包,包含编译、调试、文档工具,以及JRE
JRE Java Runtime Environment Java运行时环境,包含JVM和核心类库,用于运行Java程序
JVM Java Virtual Machine Java虚拟机,执行字节码,实现跨平台

关系:JDK包含JRE,JRE包含JVM。

第2题:请列举JDK 5引入的5个重要特性。

难度:⭐⭐

参考答案

  1. 泛型 :类型安全的集合,如List<String>

  2. 增强for循环:简化集合遍历

  3. 自动装箱/拆箱:基本类型与包装类自动转换

  4. 枚举 :类型安全的常量,如enum Color {RED, GREEN}

  5. 注解 :元数据支持,如@Override

  6. 并发包:java.util.concurrent(答案选5个即可)

第3题:Lambda表达式和Stream API如何改变Java编程?

难度:⭐⭐⭐

参考答案

Lambda表达式和Stream API将函数式编程风格引入Java,主要体现在:

  1. 代码简洁:将集合操作从"怎么做"变成"做什么"

    java

    复制代码
    // 传统方式
    List<String> result = new ArrayList<>();
    for (User u : users) {
        if (u.getAge() > 18) {
            result.add(u.getName());
        }
    }
    Collections.sort(result);
    
    // Stream方式
    List<String> result = users.stream()
        .filter(u -> u.getAge() > 18)
        .map(User::getName)
        .sorted()
        .collect(Collectors.toList());
  2. 惰性求值:Stream的中间操作不会立即执行,只有终端操作时才触发

  3. 并行处理parallelStream()可以轻松利用多核CPU

  4. 减少中间状态:无需临时变量,降低出错概率

第4题:什么是虚拟线程?它与传统平台线程有什么区别?

难度:⭐⭐⭐⭐

参考答案

虚拟线程是JDK 21正式发布的轻量级线程,由JVM管理而非操作系统。

核心区别

维度 平台线程 虚拟线程
管理方 操作系统内核 JVM
内存占用 ~1MB/线程 几KB/线程
最大数量 数千 数百万
创建开销 极低
上下文切换 系统调度 JVM调度
阻塞影响 占用操作系统线程 仅挂起自身

工作原理

  1. 虚拟线程运行在载体线程(平台线程)上

  2. 阻塞操作时自动卸载,载体线程可执行其他虚拟线程

  3. 阻塞结束后重新调度

适用场景:I/O密集型任务(Web服务器、数据库访问、RPC调用)优势明显;CPU密集型任务仍使用平台线程。

第5题:从JDK 8到21,Java的并发编程模型发生了怎样的演变?

难度:⭐⭐⭐⭐⭐

参考答案

JDK 8时代

  • 线程池Executors.newFixedThreadPool()

  • FutureExecutorService.submit()返回Future

  • CompletableFuture:异步编程组合,但仍有回调嵌套问题

java

复制代码
ExecutorService executor = Executors.newFixedThreadPool(10);
Future<String> future = executor.submit(() -> fetchData());

JDK 11时代

  • CompletableFuture增强:超时控制等

  • Flow API:响应式流规范

JDK 21革命

  • 虚拟线程:颠覆性改变,每个任务一个线程成为可能

    java

    复制代码
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        // 可以创建10万个虚拟线程
        IntStream.range(0, 100000).forEach(i -> 
            executor.submit(() -> handleRequest(i))
        );
    }
  • 结构化并发:将相关任务组织成整体,统一处理取消和错误

    java

    复制代码
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Future<String> user = scope.fork(() -> fetchUser());
        Future<Integer> order = scope.fork(() -> fetchOrder());
        scope.join();
        return new Result(user.resultNow(), order.resultNow());
    }
  • 作用域值:替代ThreadLocal,更高效的线程间数据共享

演变趋势

从"线程池管理资源"到"虚拟线程无限制创建",从"手动处理并发错误"到"结构化统一管理",从"ThreadLocal"到"作用域值"。Java的并发编程正变得越来越简单、安全、高效。

3道实战场景题

场景1:老项目升级决策

场景描述:你们公司有一个基于JDK 8开发的金融交易系统,运行稳定已有5年。现在技术部门建议升级到JDK 17或21,但业务部门担心稳定性和升级成本。作为技术负责人,你会如何分析和决策?

考察点:版本选型、风险评估、升级策略

参考思路

第一步:评估升级收益

  • JDK 17/21带来的价值:虚拟线程可提升并发能力、Records减少样板代码、新日期API简化时间处理、ZGC降低延迟

  • 如果系统是I/O密集型,虚拟线程可能带来10倍以上并发提升

第二步:识别风险点

  • 依赖第三方库是否兼容(如旧版Hibernate、MyBatis)

  • 是否有使用Java EE模块(JAXB等)被移除

  • 是否有反射访问JDK内部API

  • 团队对新特性熟悉程度

第三步:分阶段升级策略

  1. 评估阶段(2周):使用jdeprscan扫描废弃API,检查所有依赖

  2. 沙箱测试(1个月):搭建完全相同的测试环境,运行全量自动化测试

  3. 试点升级(1个月):选择非核心模块升级到JDK 17,灰度观察

  4. 分批升级(2-3个月):核心模块分批升级,每次上线前充分测试

  5. 回退预案:保留JDK 8环境,遇到严重问题立即回滚

第四步:版本选择建议

  • 如果是保守策略:升级到JDK 17(稳定,Spring Boot 3.x适配)

  • 如果是进取策略:直接升级到JDK 21(虚拟线程收益大,支持周期长)

场景2:高并发系统优化

场景描述:你的Web应用基于JDK 11开发,每天处理500万请求。高峰期出现响应变慢、CPU飙升,监控显示Tomcat线程池经常占满(最大200线程),大量请求排队。作为架构师,你会如何优化?

考察点:虚拟线程应用、线程模型理解

参考思路

问题诊断:线程池占满说明系统是I/O密集型阻塞,每个请求都长时间占用Tomcat线程。

方案一:升级到JDK 21,启用虚拟线程

yaml

复制代码
# 如果使用Spring Boot 3.2+
spring:
  threads:
    virtual:
      enabled: true

代码无需修改,Tomcat会自动使用虚拟线程处理请求:

java

复制代码
@RestController
public class OrderController {
    @GetMapping("/order/{id}")
    public Order getOrder(@PathVariable Long id) {
        // 这些阻塞调用现在运行在虚拟线程上
        User user = userService.getUser(id);      // RPC调用
        Order order = orderService.getOrder(id);  // 数据库查询
        List<Item> items = itemService.getItems(id); // 另一个服务
        return new Order(order, user, items);
    }
}

效果

  • 理论上可以支撑的并发从200提升到数万

  • 操作系统线程从200个变为几十个(载体线程)

  • 请求不再排队等待

方案二:使用结构化并发优化内部调用

java

复制代码
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) throws Exception {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Future<User> user = scope.fork(() -> userService.getUser(id));
        Future<Order> order = scope.fork(() -> orderService.getOrder(id));
        Future<List<Item>> items = scope.fork(() -> itemService.getItems(id));
        
        scope.join();          // 等待所有任务
        scope.throwIfFailed(); // 如果有失败,抛出异常
        
        return new Order(order.resultNow(), 
                        user.resultNow(), 
                        items.resultNow());
    }
}

方案三:逐步迁移策略

  1. 先将JDK升级到21,但不启用虚拟线程,验证兼容性

  2. 在测试环境启用虚拟线程,压测验证

  3. 选择非核心接口灰度开启

  4. 全量开启,监控线程数和响应时间

场景3:云原生部署优化

场景描述:你们团队准备将Java应用容器化部署到Kubernetes,但发现传统JDK 8应用启动需要30-40秒,内存占用高(基础堆1GB),导致Pod扩缩容慢、资源成本高。你会如何利用新版JDK优化?

考察点:云原生知识、AOT编译、CDS

参考思路

核心方案:升级到JDK 21或25,利用AppCDS和AOT优化

第一步:启用AppCDS(应用类数据共享)

bash

复制代码
# 1. 创建类列表
java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=app.list -jar myapp.jar

# 2. 创建共享存档
java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=app.list \
     -XX:SharedArchiveFile=app.jsa -cp myapp.jar

# 3. 使用存档启动
java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=app.jsa -jar myapp.jar

效果:启动时间减少30-40%

第二步:升级到JDK 25,利用紧凑对象头

JDK 25的对象头压缩到64位,内存占用减少20%。

第三步:使用ZGC替代G1(如果延迟敏感)

bash

复制代码
java -XX:+UseZGC -jar myapp.jar

ZGC的暂停时间通常在10ms以内,远低于G1的几十毫秒。

第四步:如果允许,使用GraalVM原生镜像

xml

复制代码
<!-- 使用native-maven-plugin -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <configuration>
        <imageName>myapp</imageName>
        <buildArgs>
            <buildArg>-H:+ReportExceptionStackTraces</buildArg>
        </buildArgs>
    </configuration>
</plugin>

bash

复制代码
mvn -Pnative native:compile

效果

  • 启动时间:40秒 → <100毫秒

  • 内存占用:1GB → 120MB

  • 镜像体积:可执行文件约50-70MB

第五步:Kubernetes配置优化

yaml

复制代码
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: app
        image: myapp:latest
        startupProbe:
          httpGet:
            path: /health
          initialDelaySeconds: 0  # 原生镜像启动极快
          periodSeconds: 1
        resources:
          requests:
            memory: "256Mi"  # 大幅降低内存请求
            cpu: "500m"
          limits:
            memory: "512Mi"
            cpu: "1000m"

最终收益

  • 启动时间:40秒 → 100毫秒(原生镜像)或 5-8秒(优化后JVM)

  • 内存占用:降低50-80%

  • 资源成本:降低60%以上

  • 扩缩容响应:从分钟级到秒级

结语

从1996年的JDK 1.0到2025年的JDK 25,Java走过了近三十年的演进历程。它从一种简单的面向对象语言,成长为拥有庞大生态的企业级平台;从传统的同步阻塞模型,进化到支持百万级并发的虚拟线程;从语法相对繁琐,到今天拥有Lambda、Records、模式匹配等现代语言特性。

每个版本都承载着那个时代的印记:1.2让Java走向企业级,5.0带来语法革命,8开启函数式编程,11完成模块化转型,21用虚拟线程重构并发模型,25则瞄准AI与云原生。

理解这些演进历程,不仅有助于我们在面试中展现技术深度,更重要的是能在实际项目中做出正确的技术选型,写出更优雅、更高效的代码。无论你还在使用JDK 8,还是已经拥抱JDK 21,希望这篇超详细的梳理能为你提供有价值的参考。

技术不断向前,但Java的核心理念始终未变:一次编写,到处运行。这或许正是它能统治企业级开发近三十年的根本原因。

参考资料

  1. Java基础(一):发展史、技术体系与JDK环境配置详解. 华为云社区, 2025.

  2. 全网最全的JAVA所有版本特性【JAVA 1.0 - JAVA 24】. CSDN博客, 2018-2025.

  3. ⚠️2025 还守 JDK8?JDK8-25 进化史 + 4 个 LTS 避坑指南. 百家号, 2025.

  4. 聊聊JDK1.0到JDK20的那些事儿. 京东云开发者社区, 2023.

  5. 聊聊 JDK1.0 到 JDK20 的那些事儿 | 京东云技术团队. 搜狐, 2023.

相关推荐
追随者永远是胜利者1 小时前
(LeetCode-Hot100)62. 不同路径
java·算法·leetcode·职场和发展·go
好学且牛逼的马1 小时前
从“XML汪洋”到“智能原生”:Spring Framework 1.x 到 7.x 演进全记录与核心知识点详解(超详细版)
java
追随者永远是胜利者1 小时前
(LeetCode-Hot100)56. 合并区间
java·算法·leetcode·职场和发展·go
追随者永远是胜利者2 小时前
(LeetCode-Hot100)55. 跳跃游戏
java·算法·leetcode·游戏·go
shangjian0072 小时前
Python基础-环境安装-Anaconda配置虚拟环境
开发语言·python
codeJinger2 小时前
【Python】函数
开发语言·python
知识即是力量ol2 小时前
Java 虚拟机:JVM篇
java·jvm·八股
快乐zbc2 小时前
苍穹外卖 - 菜品起售/停售复习笔记
java·笔记
geovindu2 小时前
python: Command Pattern
开发语言·python·命令模式