JDK 9 之后,Oracle 改了发布节奏:从"几年一个大版本"变成"每半年一个功能版"(3 月、9 月各一版)。于是 JDK 10(2018-03)、JDK 11(2018-09)紧跟着来。这两版单体特性不多,但有两个影响深远的东西:var 局部变量推断 ,以及 LTS(长期支持版)制度的确立------后者直接决定了你公司"升到哪、停在哪"。
一、var:类型推断,不是动态类型
JDK 10 最显眼的特性就是 var。它让编译器根据右边的初始值推断变量类型,你不用再写出来:
java
// 以前
ArrayList<User> users = new ArrayList<>();
Map<String, List<Integer>> map = new HashMap<>();
// JDK 10+
var users = new ArrayList<User>();
var map = new HashMap<String, List<Integer>>();
但务必澄清一个误解:var 不是 JavaScript 那种动态类型 。var 只是"让我少打几个字",变量类型在编译期就确定了,是强类型、不可变类型。下面这样会编译失败:
java
var x = 10;
x = "hello"; // 编译错误:x 已被推断为 int
var 的边界(哪里能用、不能用)
| 能用 | 不能用 |
|---|---|
| 局部变量(有初始化) | 方法参数、返回值、字段 |
| for 循环变量 | 没有初始化表达式(var x;) |
| try-with-resources 资源 | lambda 参数(JDK 11 起 lambda 参数可隐式类型,但不用 var 关键字) |
| 增强 for 迭代变量 | 数组初始化器(var a = {1,2} 不行,类型不明) |
什么时候该用 var,什么时候不该
经验法则:var 提升可读性就用,损害可读性就不用。
- 类型在右手一目了然时(构造器、
List.of),用var更清爽。 - 类型不明显时(方法返回值、魔法数字
var x = compute()),老老实实写类型,否则读代码要回跳找声明。
java
var list = new ArrayList<String>(); // 好:类型一眼看出
var result = service.query(); // 差:得去查 query 返回啥
二、LTS 制度:为什么"升到 11 还是 8"是个真问题
这是 JDK 10/11 里比 var 重要十倍的事。Oracle 的新规则:
- 每 6 个月一个功能版 ,但只有每三年一个版本是 LTS(Long-Term Support),会得到多年的安全与 bug 修复。
- JDK 8 是事实上的 LTS(虽早于此制度,但长期维护)。
- JDK 11 是第一个按新制度发布的 LTS,接着是 17、21......
- 非 LTS 版本(10、12、13、14、15、16、18、19、20 等)只有半年支持,很快 EOL。
这意味着企业升级策略变成:在 LTS 之间跳跃(8 → 11 → 17 → 21),而不是追每个小版本。选 LTS 的理由是------云厂商、Spring、中间件都只对 LTS 做充分验证;选非 LTS 等于自己当小白鼠。
一句话决策:生产系统优先盯 LTS。2026 年的今天,新项目起点建议 21(最新 LTS);老系统至少升到 17。
三、JDK 11 的其他硬货
JDK 11 作为 LTS,攒了不少实用特性:
1. 单文件源码直接运行
不用先 javac 再 java,直接 java Hello.java 就能跑(编译器在内存里编译后执行)。小脚本、教学 demo 省一步。
2. 全新的 HTTP 客户端(转正)
JDK 9 孵化的 jdk.incubator.http 在 11 转正为 java.net.http,支持 HTTP/2、WebSocket,API 比老 HttpURLConnection 现代得多:
java
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/user"))
.header("Accept", "application/json")
.build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.statusCode() + " -> " + resp.body());
// 异步也很自然
client.sendAsync(req, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
3. 字符串增强
java
" hello ".strip(); // 去首尾空白(识别 Unicode 空白,比 trim 更准)
" hello ".stripIndent(); // 去公共缩进(配合文本块,见 12/13 篇)
"a".repeat(3); // "aaa"
"java".isBlank(); // false;" ".isBlank() true(全空白判断)
4. 集合与 Optional 小补
List.of/Set.of/Map.of在 9 引入,11 稳定。Optional加isEmpty()、ifPresentOrElse、or、stream()。String底层完成紧凑化:纯 Latin-1 字符用 1 字节存,内存直接省一半(9 引入,11 打磨)。
5. 性能与 GC
- ZGC(实验) 和 Epsilon(无操作 GC) 引入:ZGC 目标亚毫秒停顿、支持超大堆;Epsilon 用于测试/极短生命周期场景。
- G1 持续调优,成为实至名归的默认主力。
6. 移除了什么
JDK 11 删掉了 Java EE 和 CORBA 模块 (java.xml.bind 等 JAXB 被移除),也删了 javafx(改由 OpenJFX 独立维护),还去掉了 Nashorn JavaScript 引擎(后续版彻底移除)。这意味着老项目如果从 JDK 8 升级到 11,若用到 javax.xml.bind 等,必须手动加第三方依赖(如 jakarta.xml.bind),这是迁移第一大坑。
四、本篇记住的清单
var是编译期类型推断,仍是强类型,不能用于参数/返回值/字段。var提升可读性就用,类型不明时老实干写类型。- LTS 制度:8/11/17/21 是 LTS;企业只在 LTS 间跳跃升级。
- JDK 11 可
java X.java直接跑单文件源码。 java.net.http全新 HTTP 客户端,支持 HTTP/2 与异步。- 字符串
strip/isBlank/repeat比trim更现代。 - JDK 11 移除 JAXB/CORBA/JavaFX/Nashorn------8 升 11 迁移的第一类雷。
- ZGC、Epsilon 在 11 进场(实验)。
理解了 var 和 LTS,你就掌握了"现代 Java 升级节奏"的开关。下一篇 JDK 12/13:var 之后,文本块和 switch 表达式又让代码清爽了一截。