深度解析塑造未来的技术文章。

Java 一直在进步,只是没人注意到

Java 每六个月发布一次,悄悄改变了这门语言。模式匹配、虚拟线程和 Record 让它几乎认不出来了。

一杯发光的咖啡杯,将陈旧昏暗的老式办公室变成明亮现代的空间

Java 是编程语言界的“小透明”,总是得不到应有的尊重。问问三十岁以下的开发者提到 Java 会想到什么,他们脑海里浮现的大概是 XML 配置文件、企业级 Bean 和 AbstractSingletonProxyFactoryBean。这门语言的口碑停留在 2008 年,那时 Java 6 是最新版本,写 Java 就意味着被样板代码淹没。

但自 2017 年 Java 改为每六个月发布一次以来,发生了一件了不起的事。语言的演进速度,是 Java 6、7、8 之间那四年空档期里无法想象的。Record、密封类、模式匹配、虚拟线程、字符串模板、结构化并发……每个版本都在让 Java 代码变得更简洁、更有表现力,也更让人愉快。Java 26 延续了这个势头,“现在的 Java”和“大家想象中的 Java”之间的差距从未如此之大。

彻底改变一切的特性

如果你自 Java 8 之后就没碰过 Java,那你错过的是整整十年的改进。下面看看现在的 Java 长什么样。

Record(Java 14)是不可变的数据类。过去需要五十行样板代码的东西——字段、构造器、getter、equals、hashCode、toString——现在一行就够了。

// Java 8: 50 lines of boilerplate
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Point)) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() { return Objects.hash(x, y); }
@Override
public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}
// Java 14+: one line
record Point(int x, int y) {}

模式匹配(Java 16 至 21,仍在演进)允许你在 switch 表达式和 instanceof 检查中解构对象。这消除了那些困扰 Java 代码的层层嵌套 if-else-instanceof 判断链。

// Pattern matching with sealed types and records
sealed interface Shape permits Circle, Rectangle, Triangle {}
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}
record Triangle(double base, double height) implements Shape {}
// Exhaustive pattern matching — compiler verifies all cases covered
double area(Shape shape) {
return switch (shape) {
case Circle(var r) -> Math.PI * r * r;
case Rectangle(var w, var h) -> w * h;
case Triangle(var b, var h) -> 0.5 * b * h;
};
}

虚拟线程(Java 21)是由 JVM 而非操作系统管理的轻量级线程。你可以创建数百万个虚拟线程,而不会耗尽内存或操作系统的线程句柄。这让“每个请求一个线程”这种最简单的并发模型,在大规模场景下也变得切实可行。

// Create a million concurrent tasks — try this with OS threads
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 1_000_000).forEach(i -> {
executor.submit(() -> {
// Each task gets its own virtual thread
// Blocking I/O (HTTP, DB, file) automatically yields
var response = httpClient.send(request, BodyHandlers.ofString());
return response.body();
});
});
}
// All done — executor auto-closes, waits for completion

虚拟线程是 Java 对 Go 的 goroutine 和 Kotlin 协程的回应。区别在于:你不需要特殊语法,也不必给函数“染色”。普通的阻塞式代码照常工作,JVM 会在阻塞点透明地挂起和恢复虚拟线程。你现有的阻塞式 HTTP 客户端、JDBC 驱动和文件 I/O 代码,无需修改就能受益。

为什么没人注意到

Java 的改进轨迹确实令人印象深刻。那为什么大家的印象还停留在 2008 年?

首先是企业惯性。很多组织仍在运行 Java 8 或 11,因为升级一个庞大的代码库既昂贵又有风险。开发者说起“Java”时,指的往往是公司正在用的版本,而不是最新发布的版本。Java 8 的开发者根本没体验过 Record、模式匹配或虚拟线程。

其次是框架层。Spring Boot、Jakarta EE 以及其他企业级框架会在语言之上叠加它们自己的复杂性。开发者常把 Spring 那套繁重的注解仪式怪在“Java”头上,但那是框架的问题,不是语言的问题。脱离重型框架的现代 Java,其实精简得出人意料。

第三是文化惯性。技术圈喜欢给语言划分阵营。Java 被贴上“企业级”的标签,与大公司、繁琐的流程和过度设计联系在一起。这种口碑并非无中生有——2005 年前后的 Java 开发确实该受批评。但口碑往往比它的成因多存活好几年。

JVM 的优势

语言层面的改进固然重要,但 Java 最深厚的优势依然是 JVM。经过三十年的优化,HotSpot JVM 已经是有史以来最成熟的运行时环境之一。

  • JIT 编译。JVM 会分析正在运行的代码,并将热点方法编译为优化后的本地代码。对于长时间运行的应用(服务器、数据处理),JVM 的性能往往能与 C++ 相当甚至更优,因为 JIT 可以针对实际运行时的行为进行优化,而不只是依赖静态分析。
  • 垃圾回收。现代 GC 实现(ZGC、Shenandoah)即使在堆达到 TB 级别时,也能将停顿时间控制在亚毫秒级。“GC 停顿拖垮延迟”这一批评已经多年不成立了,因为这些收集器可以与应用程序并发运行。
  • 可观测性。JFR(Java Flight Recorder)提供了对生产环境安全的性能分析,开销几乎可以忽略。你可以分析 CPU 使用、内存分配、锁竞争、I/O 延迟和 GC 行为,而无需拖慢应用。
  • 生态成熟。Maven Central 上有数百万个软件包。构建工具(Gradle、Maven)已被充分理解。测试生态(JUnit、Mockito、Testcontainers)非常出色。部署方案(容器、GraalVM 原生镜像)也有了长足的发展。

GraalVM 与原生镜像

Java 最大的短板一直是启动时间。JVM 初始化、加载类以及预热 JIT 编译器需要数百毫秒。对于 Serverless 函数和命令行工具来说,这样的开销难以接受。

GraalVM Native Image 的解决思路是提前编译(AOT),把 Java 应用编译成独立的可执行文件,运行时无需 JVM。启动时间可以从数百毫秒降到个位数毫秒。内存占用也随之下降,因为既没有解释器,也没有 JIT 编译器,更不需要加载类元数据。

代价是:原生镜像失去了 JVM 的自适应优化能力。经过 JIT 编译的应用会随着 JVM 学习热点路径而越跑越快,而原生镜像启动很快,却不会再变得更快。对于短生命周期的进程(命令行工具、Serverless),原生镜像更有优势;而对于长时间运行的服务器,JVM 的 JIT 最终能生成更快的代码。关键是根据运行时特征选择合适的工具。

现代 Java 对比其他选择

那些被认为“取代”了 Java 的语言——Go、Kotlin、Rust——都很优秀。但现代 Java 的竞争力比人们想象的要强。

Go 的简洁很有吸引力,但 Java 现在有了虚拟线程(与 goroutine 相当),而且类型系统丰富得多。类型系统对大型代码库至关重要,而 Java 的类型系统——尤其是密封类型和模式匹配——能捕获 Go 接口系统遗漏的错误。

Kotlin 在语法上比 Java 更精炼,但它运行在同一个 JVM 上。Kotlin 相较于 Java 8 的大部分优势(空安全、数据类、协程)在现代 Java 中都有了部分或完整的对应方案(Optional 模式、Record、虚拟线程)。Kotlin 依然更简洁,但差距已经明显缩小。

Rust 占据的是另一个生态位——没有垃圾回收器的系统编程。Java 不必去那里竞争。但在服务端应用、数据处理和企业软件领域,Java 在性能、安全性和生态成熟度上的组合,很难被超越。

真正的创新是六个月的发布节奏

比起任何单一特性,六个月一次的发布周期才真正改变了 Java 的走向。2017 年之前,Java 版本以三到四年为周期,总想一次性塞进所有东西。功能没准备好就被推迟,导致整个版本延期,又迫使下一个版本必须包含更多内容,结果下一个版本也跟着延期。Java 9 之所以拖了三年多,部分原因就在于模块系统过于复杂。

有了六个月的发布节奏,功能准备好了就发布。模式匹配从 Java 16 到 24 逐步交付,每个版本只增加一块。虚拟线程在最终交付前经历了两轮预览版。这种渐进式的做法让 Java 团队能在确定永久性 API 之前,收集真实世界的反馈并调整设计。

这个道理不只适用于 Java:小而频繁的发布会累积成翻天覆地的变化。自 17 版本以来,Java 的每一次发布单独看都算不上惊艳,但合在一起,它们已经重塑了这门语言。如果你五年前试过 Java,却被它的冗长劝退,不妨再试一次,你可能会惊喜地发现不一样的东西。