前言
2014 年 JDK 8 的发布无疑是 Java 历史的里程碑,Lambda 表达式和 Stream API 让 Java 正式拥抱函数式编程。然而在此之后,Java 的发布节奏发生了根本性变化——从 2018 年起,Oracle 采用“每 6 个月一个版本”的快节奏发布策略。JDK 17 于 2021 年 9 月发布,是继 JDK 8 和 JDK 11 之后的第三个 LTS(长期支持)版本,提供长达 8 年的官方支持。
从 JDK 8 到 JDK 17,Java 平台由约194 个 JEP(JDK Enhancement Proposals)塑造而成(根据官方 JEP 索引统计,部分草案被合并或撤回,实际数量略有浮动)。本文将从语言特性、API 更新和性能优化三个维度,梳理这其中最值得关注的新特性,包含每个特性的首次引入版本、正式定稿版本、对应 JEP 编号,以及官方文档和代码示例。
一、语言特性篇
1. 局部变量类型推断——var(JEP 286)
var关键字允许编译器根据初始化表达式推断局部变量的类型,减少了冗余的类型声明。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 10 Early Access(需--enable-preview) |
| 正式定稿 | JDK 10 正式版 |
| JEP | JEP 286: Local-Variable Type Inference |
| 官方文档 | Local Variable Type Inference |
// JDK 8 写法List<String>list=newArrayList<String>();Map<String,List<Integer>>map=newHashMap<String,List<Integer>>();// JDK 10+ 写法varlist=newArrayList<String>();varmap=newHashMap<String,List<Integer>>();vargreeting="Hello World!";// 推断为 String注意:
var只能用于局部变量,不能用于字段、方法参数或返回类型。
2. Switch 表达式(JEP 325 → JEP 354 → JEP 361)
传统的switch是语句(statement),而 Switch 表达式(expression)可以直接返回一个值,配合箭头语法->和yield,代码更加简洁安全。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 12(JEP 325) |
| 第二次预览 | JDK 13(JEP 354) |
| 正式定稿 | JDK 14(JEP 361) |
| 官方文档 | Java Language Changes Summary |
// JDK 8 写法(语句)intnumLetters;switch(day){caseMONDAY:caseFRIDAY:caseSUNDAY:numLetters=6;break;caseTUESDAY:numLetters=7;break;default:numLetters=0;}// JDK 14+ 写法(表达式)intnumLetters=switch(day){caseMONDAY,FRIDAY,SUNDAY->6;caseTUESDAY->7;default->{Strings=day.toString();yields.length();// yield 返回值}};3. 文本块(Text Blocks)(JEP 355 → JEP 368 → JEP 378)
文本块解决了 Java 中多行字符串拼接的痛点,无需大量转义字符即可书写格式化的多行文本。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 13(JEP 355) |
| 第二次预览 | JDK 14(JEP 368) |
| 正式定稿 | JDK 15(JEP 378) |
| 官方文档 | Java Language Changes Summary |
// JDK 8 写法Stringhtml="<html>\n"+" <body>\n"+" <p>Hello, world</p>\n"+" </body>\n"+"</html>";// JDK 15+ 写法(文本块)Stringhtml=""" <html> <body> <p>Hello, world</p> </body> </html> """;4.instanceof模式匹配(JEP 305 → JEP 375 → JEP 394)
将“类型检查”和“类型转换”合二为一,这是你在问题中提到的语法。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 14(JEP 305) |
| 第二次预览 | JDK 15(JEP 375) |
| 正式定稿 | JDK 16(JEP 394) |
| 官方文档 | Pattern Matching for instanceof |
// JDK 8 写法if(objinstanceofString){Strings=(String)obj;System.out.println(s.toUpperCase());}// JDK 16+ 写法(模式匹配)if(objinstanceofStrings){System.out.println(s.toUpperCase());}// 配合 && 使用if(objinstanceofStrings&&s.length()>5){System.out.println("长度大于5: "+s.toUpperCase());}5. Record 类(JEP 359 → JEP 384 → JEP 395)
Record 是一种透明的数据载体,编译器自动生成构造器、equals()、hashCode()和toString(),彻底告别 POJO 的模板代码。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 14(JEP 359) |
| 第二次预览 | JDK 15(JEP 384) |
| 正式定稿 | JDK 16(JEP 395) |
| 官方文档 | Record Classes |
// JDK 8 写法(需要 50+ 行代码)publicclassPerson{privatefinalStringname;privatefinalintage;publicPerson(Stringname,intage){this.name=name;this.age=age;}publicStringgetName(){returnname;}publicintgetAge(){returnage;}// equals()、hashCode()、toString() 等...}// JDK 16+ 写法(1 行搞定)publicrecordPerson(Stringname,intage){}// 使用varperson=newPerson("Alice",30);System.out.println(person.name());// 访问器方法自动生成System.out.println(person.age());6. 密封类(Sealed Classes)(JEP 360 → JEP 397 → JEP 409)
密封类允许开发者限制哪些类可以继承或实现某个类/接口,提供更精细的继承控制。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 15(JEP 360) |
| 第二次预览 | JDK 16(JEP 397) |
| 正式定稿 | JDK 17(JEP 409) |
| 官方文档 | Sealed Classes |
// 定义一个密封类 Shape,只允许 Circle 和 Rectangle 继承publicabstractsealedclassShapepermitsCircle,Rectangle{// ...}publicfinalclassCircleextendsShape{privatefinaldoubleradius;publicCircle(doubleradius){this.radius=radius;}publicdoubleradius(){returnradius;}}publicfinalclassRectangleextendsShape{privatefinaldoublewidth,height;publicRectangle(doublewidth,doubleheight){this.width=width;this.height=height;}publicdoublewidth(){returnwidth;}publicdoubleheight(){returnheight;}}// Triangle 尝试继承 Shape → 编译错误publicfinalclassTriangleextendsShape{}// Compile error注意:密封类与 Switch 表达式配合使用时,若 Switch 模式匹配(见第 7 节)处于预览状态,代码需要
--enable-preview参数才能编译和运行。以下示例综合了两项特性:
// 需要 JDK 17 + --enable-preview(因为 Switch 模式匹配为预览特性)doublearea=switch(shape){caseCirclec->Math.PI*Math.pow(c.radius(),2);caseRectangler->r.width()*r.height();// 无需 default,编译器知道所有情况已覆盖};7. Switch 的模式匹配(JEP 406 → JEP 420 → JEP 427 → JEP 433)
将模式匹配从instanceof扩展到switch,支持类型模式、守卫模式等,是 Java 模式匹配能力的重要延伸。
重要说明:本节讨论的 Switch 模式匹配特性虽在 JDK 17 中首次预览(JEP 406),但正式定稿在 JDK 21(JEP 441)。本博客以 JDK 17 LTS 为核心,但为了展示语法演进全貌,特此收录。以下代码示例基于 JDK 17 预览版编写,必须配合
--enable-preview参数使用。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 17(JEP 406) |
| 第二次预览 | JDK 18(JEP 420) |
| 第三次预览 | JDK 19(JEP 427) |
| 第四次预览 | JDK 20(JEP 433) |
| 正式定稿 | JDK 21(JEP 441)—注:超出 JDK 17 范围 |
| 官方文档 | Pattern Matching for Switch |
// JDK 17(预览)支持类型模式和守卫模式Stringformatted=switch(obj){caseIntegeri&&i>10->String.format("大整数 %d",i);caseIntegeri->String.format("小整数 %d",i);caseLongl->String.format("长整数 %d",l);casenull->"null";default->obj.toString();};8. 接口私有方法(JEP 213——Milling Project Coin)
JDK 9 允许在接口中定义private方法,用于在default方法之间共享公共代码。该特性直接定稿,无预览阶段。
| 项目 | 内容 |
|---|---|
| 首次引入(正式定稿) | JDK 9 |
| JEP | JEP 213: Milling Project Coin |
| 官方文档 | Java Language Changes for Java SE 9 |
publicinterfaceCalculator{defaultintadd(inta,intb){returnprocess(a,b,"add");}defaultintsubtract(inta,intb){returnprocess(a,b,"subtract");}// 私有方法,供 default 方法复用privateintprocess(inta,intb,Stringoperation){System.out.println("执行: "+operation);returnoperation.equals("add")?a+b:a-b;}}二、API 与库更新篇
9. 工厂方法创建不可变集合(JEP 269)
JDK 9 为List、Set、Map提供了便捷的静态工厂方法,快速创建不可变集合。该特性直接定稿,无预览阶段。
| 项目 | 内容 |
|---|---|
| 首次引入(正式定稿) | JDK 9 |
| JEP | JEP 269: Convenience Factory Methods for Collections |
// JDK 8 写法List<String>list=newArrayList<>();list.add("a");list.add("b");list.add("c");list=Collections.unmodifiableList(list);// JDK 9+ 写法List<String>list=List.of("a","b","c");Set<String>set=Set.of("x","y","z");Map<String,Integer>map=Map.of("key1",1,"key2",2);10. 增强的伪随机数生成器(JEP 356 → JEP 371)
JDK 17 引入了新的伪随机数生成器 API(RandomGenerator接口),提供了更强大、更灵活的随机数生成能力。注意:该 API 在 JDK 17 中为预览特性。
| 项目 | 内容 |
|---|---|
| 首次引入(预览) | JDK 17(JEP 356) |
| 正式定稿 | JDK 18(JEP 371) |
| 官方文档 | JDK 17 Release Notes |
// 旧方式Randomrandom=newRandom();intvalue=random.nextInt(100);// JDK 17+(预览)新方式,JDK 18 正式可用RandomGeneratorgenerator=RandomGenerator.of("L128X1024MixRandom");intvalue=generator.nextInt(100);11. 有用的 NullPointerException(JEP 358)
JDK 14 开始,NullPointerException会精确指出哪个变量为null,极大提升了调试效率。该特性直接定稿,无预览阶段。
| 项目 | 内容 |
|---|---|
| 首次引入(正式定稿) | JDK 14 |
| JEP | JEP 358: Helpful NullPointerExceptions |
// 代码a.b.c.i=99;// JDK 8 的异常信息Exceptionin thread"main"java.lang.NullPointerExceptionatTest.main(Test.java:3)// JDK 14+ 的异常信息(精确指出 null 的位置)Exceptionin thread"main"java.lang.NullPointerException:Cannotread field"c"because"a.b"isnullatTest.main(Test.java:3)12. JShell——REPL 工具(JEP 222)
JDK 9 引入了 JShell,一个交互式的 Java REPL(Read-Eval-Print Loop)工具,允许开发者在不编写完整类的情况下快速测试代码片段。该特性直接定稿,无预览阶段。
| 项目 | 内容 |
|---|---|
| 首次引入(正式定稿) | JDK 9 |
| JEP | JEP 222: JShell: The Java Shell |
$ jshell|欢迎使用 JShell -- 版本17|要大致了解该版本,请键入:/help intro jshell>var list=List.of(1,2,3)list==>[1,2,3]jshell>list.stream().map(n ->n *2).toList()$2==>[2,4,6]三、性能与运行时篇
13. Java 平台模块系统(Project Jigsaw)(JEP 261)
JDK 9 最重大的变化——模块化系统(JPMS),将 JDK 本身模块化,并允许开发者创建自己的模块。该特性直接定稿,无预览阶段。
| 项目 | 内容 |
|---|---|
| 首次引入(正式定稿) | JDK 9 |
| JEP | JEP 261: Module System |
| 官方文档 | Project Jigsaw |
// module-info.javamodulecom.example.myapp{requiresjava.sql;requireshu.advancedweb.somedependency;exportscom.example.myapp.api;}14. ZGC——可扩展的低延迟垃圾回收器(JEP 333 → JEP 377)
ZGC 是一种可扩展的低延迟垃圾回收器,暂停时间不超过 10 毫秒,且不随堆大小增加而增加。
| 项目 | 内容 |
|---|---|
| 首次引入(实验性) | JDK 11(JEP 333) |
| 正式产品化 | JDK 15(JEP 377) |
| 官方文档 | ZGC: The Z Garbage Collector |
# 启用 ZGC(需显式指定)java-XX:+UseZGC-jarmyapp.jar关于“默认”的澄清:JDK 17默认增强了对 ZGC 和 Shenandoah 的支持(如性能优化和平台兼容性提升),但垃圾回收器本身并未默认启用。开发者仍需通过
-XX:+UseZGC显式开启,方可实现毫秒级停顿。
15. 强封装 JDK 内部 API(JEP 403)
从 JDK 17 开始,JDK 内部 API(如sun.misc.Unsafe)被默认强封装,应用程序无法再通过反射随意访问。
| 项目 | 内容 |
|---|---|
| 首次引入(正式定稿) | JDK 17 |
| JEP | JEP 403: Strongly Encapsulate JDK Internals |
| 官方文档 | Oracle JDK Migration Guide |
# 注意:--illegal-access 参数在 JDK 16 中已被标记为废弃(deprecated),# JDK 17 中默认行为已从 permit 改为 deny,并在 JDK 18 中被彻底移除。# 更推荐通过 --add-opens 等模块参数精确控制访问权限。# 不推荐(JDK 16+ 废弃警告,JDK 17 默认 deny,JDK 18+ 报错)java--illegal-access=permit-jarmyapp.jar# 推荐方式(精确开放特定包)java--add-opens java.base/java.lang=ALL-UNNAMED-jarmyapp.jar四、各版本 JEP 数量一览
| 版本 | 发布时间 | 是否 LTS | 主要 JEP 数量 |
|---|---|---|---|
| JDK 8 | 2014.03 | ✅ LTS | 56 |
| JDK 9 | 2017.09 | ❌ | — |
| JDK 10 | 2018.03 | ❌ | — |
| JDK 11 | 2018.09 | ✅ LTS | 17 |
| JDK 12 | 2019.03 | ❌ | — |
| JDK 13 | 2019.09 | ❌ | — |
| JDK 14 | 2020.03 | ❌ | — |
| JDK 15 | 2020.09 | ❌ | — |
| JDK 16 | 2021.03 | ❌ | — |
| JDK 17 | 2021.09 | ✅ LTS | 14 |
从 JDK 8 到 JDK 17,共涉及约 194 个 JEP(具体数字因统计口径略有浮动)。
五、迁移建议
从 JDK 8 升级到 JDK 17 并非没有挑战,但也带来了显著收益:
| 维度 | 提升 |
|---|---|
| 启动速度 | 提升 30%+ |
| 内存占用 | 降低 20%+ |
| 代码量 | 借助 Record、var 等特性可减少 40%+ |
| GC 停顿 | ZGC 实现毫秒级停顿 |
需要注意的兼容性问题:
- JDK 内部 API(如
sun.misc.Unsafe)被强封装,依赖这些 API 的库可能需要升级,废弃的--illegal-access参数需替换为--add-opens。 - 模块化系统可能影响类路径上的某些遗留代码。
- Spring Boot 3.x 已强制要求 JDK 17+。
参考资源
- OpenJDK JEP 索引:https://openjdk.org/jeps/
- Oracle Java SE 17 文档:https://docs.oracle.com/en/java/javase/17/
- Java Language Changes Summary:https://docs.oracle.com/en/java/javase/17/language/java-language-changes-summary.html
- Java SE 17 规范(JSR 392):https://cr.openjdk.java.net/~iris/se/17/latestSpec/
- JDK 17 Release Notes:https://docs.oracle.com/en/java/javase/17/docs/api/
结语
JDK 17 不仅是 Java 8 之后最重要的 LTS 版本,更代表了 Java 语言演进的“新常态”——从过去的“大版本大更新”转向“小步快跑、持续交付”。语法层面,var、Record、Sealed Class、模式匹配等特性让 Java 代码更加简洁、安全;运行时层面,ZGC、模块化、强封装内部 API 等改进让 Java 在现代云原生环境中更具竞争力。
如果你还在使用 JDK 8,是时候认真考虑升级到 JDK 17 了——这不仅是一次版本升级,更是一次开发范式的跃迁。