Java:被唱衰了十五年,还在扛着最重的业务
一个少见的样本
2010 年前后,"Java 要死了"是技术社区的流行论调。十几年过去,2026 年再看:全球大部分银行核心系统、电商交易链路、企业 ERP、大数据栈(Hadoop、Spark、Flink、Kafka、Elasticsearch 全靠 JVM)、Android 早期的官方语言,都还是 Java。TIOBE 榜上它长期在前四。它没死,它甚至变得更好了。
原因不复杂:Java 的竞争力从来不是"先进",而是"在被大规模、长时间、多人协作地使用时不崩"。它把可控性、向后兼容、成熟的工具链和庞大的中间件生态做到了极致。这些东西在业务系统里的权重,远高于语言本身新不新潮。
三十年,几次关键转折
- 1995:Sun 发布,James Gosling 主导。最初叫 Oak,为机顶盒设计,结果机顶盒项目黄了,语言活了下来。
- 1996:JDK 1.0,"Write Once, Run Anywhere"——靠 JVM 抹平平台差异,这句口号当时是真的有震撼力。
- 2004:Java 5 引入泛型、注解、自动装箱、枚举、for-each。这次更新奠定了现代 Java 的语法基础。
- 2009:Sun 被 Oracle 收购。社区一片哀嚎,实际结果是 JVM 生态反而被 Oracle 更强势地推了起来。
- 2014:Java 8 引入 Lambda 和 Stream。这是继 Java 5 之后最重要的一次更新,"函数式"终于可用。
- 2017 之后:模块化(Java 9 的 Jigsaw,折腾了好几年)、var(Java 10)、switch 表达式(14)、record(16)、sealed class(17)、pattern matching(21)、虚拟线程(21)。节奏从"三年一大版"变成"半年一版"。
当前版本状况
- Java 21 LTS(2023.9):虚拟线程、record patterns、switch 模式匹配、分代 ZGC。
- Java 25 LTS(2025.9):实例 main 方法(不写类也能跑)、紧凑源文件、作用域值(Scoped Values)、结构化并发(预览转正方向)、移除 32 位 x86 支持。
- Java 17 LTS 仍在大量生产使用,是企业"要不要升"的常见分界线。
LTS 版本对生产最重要:Spring Boot 3.x 要求 Java 17 起,主流的云厂商托管运行时基本都覆盖 21。
语言本身:那些真正决定质量的特性
1. 泛型是"擦除"的,这点必须清楚
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass()); // true,运行时都是 ArrayList
Java 的泛型只在编译期存在,运行时被擦除成 Object。后果:不能 new T[]、不能 instanceof List<String>、不能对基本类型泛型化(只能靠装箱和后来的 List<Integer>)。这也是为什么需要 List.of() 这种工厂方法,以及为什么很多 API 里要传 Class<T> 参数。
2. record 与 sealed:把"值对象"做对了
public record OrderLine(String sku, int qty, long unitPriceCents) {
public OrderLine {
if (qty <= 0) throw new IllegalArgumentException("qty must be > 0");
}
public long subtotal() { return (long) qty * unitPriceCents; }
}
一行声明,自动生成构造器、equals、hashCode、toString、访问器。加上 sealed interface 和模式匹配,可以写出非常干净的状态建模:
public sealed interface Shape permits Circle, Rect {}
public record Circle(double r) implements Shape {}
public record Rect(double w, double h) implements Shape {}
static double area(Shape s) {
return switch (s) {
case Circle c -> Math.PI * c.r() * c.r();
case Rect r -> r.w() * r.h();
};
}
这个 switch 是穷尽性检查的——少写一个分支编译器会报错。这是 Java 这几年最实用的一组新特性,对领域建模的收益非常直接。
3. 虚拟线程:薄弱的并发模型终于被补上了
Java 21 的虚拟线程(Virtual Threads,Project Loom)是最值得写的一条。传统线程映射到 OS 线程,一个线程栈约 1MB,几千个就撑不住;虚拟线程由 JVM 调度,可以开出百万级。
// 传统:线程池,IO 阻塞时线程被占住
try (var pool = Executors.newVirtualThreadPerTaskExecutor()) {
for (var url : urls) {
pool.submit(() -> {
String body = httpGet(url); // 阻塞调用,但底层会被挂起,不占 OS 线程
parse(body);
});
}
}
它的意义是:不用再为了吞吐去写回调地狱或 Reactor 那一套,同步阻塞式的代码就能拿到异步的并发度。这直接削弱了 Node.js / 异步框架在 IO 密集场景的"写法优势"。
代价与坑:synchronized 块里阻塞会 pin(钉住)载体线程(Java 21/22 有这个问题,24 起大幅改善);ThreadLocal 在虚拟线程上滥用会撑爆内存(所以有了 Scoped Values);连接池大小要重新算——以前"线程数=池大小"的直觉不再成立。
4. 内存模型与 GC:能调,但要会调
JVM 的 GC 是它最强的能力之一:
| 收集器 | 特点 | 适用 |
|---|---|---|
| G1 | 默认,可预测停顿目标 | 大多数服务 |
| ZGC | 停顿 < 1ms,堆可到 TB 级 | 大堆、低延迟 |
| Shenandoah | 低停顿,非 Oracle 主导 | 对停顿敏感 |
| Parallel | 吞吐优先 | 批处理、离线计算 |
常用参数(生产里基本这几条):
java -Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/app/heap.hprof \
-jar app.jar
经验:-Xms 和 -Xmx 设成一样,避免堆伸缩带来的抖动;开 heap dump 保命;上生产前用 jcmd / jstack / jmap 熟悉一遍(JDK 自带的这些工具比很多第三方的都好用)。
工程生态:它真正赢的地方
- Spring Boot:一句话起一个带 HTTP、JSON、ORM、健康检查、指标的服务。约定优于配置,生态无敌。
- 中间件:Kafka、RocketMQ、Elasticsearch、ZooKeeper、Nacos、Seata——这些构建分布式系统的核心组件,绝大多数是 JVM 系。
- 构建:Maven 稳、Gradle 灵活(Kotlin DSL 写构建脚本后体验好很多)。新项目多用 Gradle,企业存量多用 Maven。
- 测试:JUnit 5 + Mockito + Testcontainers(用真实 Docker 起 MySQL/Redis 做集成测试,这个真的改变了信心水平)。
- 可观测:Micrometer + Prometheus + Grafana 是标准搭配;Arthas 做线上诊断极强。
它的短板,说实话
- 冷启动慢。JVM 预热是硬伤:一个 Spring Boot 应用启动 10~30 秒,首请求延迟高。所以有了 GraalVM Native Image(启动到毫秒级),但构建慢、反射/动态代理要额外配置、生态适配不完整。
- 内存占用高。空跑一个 Spring Boot 就要几百 MB。
- 语法仍偏啰嗦。虽然有 var、record、文本块,但和 Kotlin 比还是重。这也是 Kotlin 在 Android 和部分后端流行起来的原因。
- 版本升级成本。从 8 升 17/21 是老项目最头疼的活,尤其涉及模块化、
javax→jakarta命名空间迁移、第三方库不兼容。
什么时候该选它
选中:企业级业务系统、金融交易、需要强一致和成熟中间件的大型分布式系统、大数据平台、需要长期维护(十年尺度)的项目、团队里有大量存量 Java 能力。
不选:极简的 Serverless 函数(冷启动是灾难)、对内存极度敏感的容器化边缘部署、需要极致低延迟(微秒级)的场景、纯粹的命令行小工具(Go/Rust 更轻)。
一句话:Java 赢在"不出意外"。当业务规模大到"一个线上事故的代价远高于开发效率"的时候,这套东西的价值就显出来了。