JDK 12~16 新特性:语法糖大丰收
你有没有觉得 Java 语法太啰嗦?
每次写 instanceof 后还要强制类型转换,写个多行字符串要一堆转义符,switch 不加 break 就「穿透」到天荒地老——这些问题,从 JDK 12 开始,终于有了答案。
JDK 12:Switch 表达式登场
Switch 表达式(Preview → JDK 14 正式)
传统 switch 的痛点:忘记写 break,就变成了「贯穿」(Fall-Through):
java
// JDK 11 及之前
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break; // 漏写就悲剧
case TUESDAY:
System.out.println(7);
break;
default:
System.out.println(0);
}JDK 12 引入箭头语法,简洁又安全:
java
// JDK 12+
int num = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6; // 多值合并,不需要 break
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
default -> 0;
};两种语法对比:
| 特性 | 冒号语法 | 箭头语法 |
|---|---|---|
| break | 必须(否则穿透) | 不需要 |
| 多值合并 | 逗号分隔 | 逗号分隔 |
| 返回值 | 需要变量赋值 | 直接返回值 |
| 块语句 | 不支持 | 支持(可用 yield) |
G1 改进
JDK 12 对 G1 垃圾收集器做了两项重要改进:
- NUMA 感知内存分配:优化多插槽服务器的内存分配
- Uncommit:立即释放未使用的堆内存给操作系统
bash
# JDK 12+ 自动启用未使用内存归还
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 myapp.jarJDK 13:Text Block 与 yield
文本块(Text Block)Preview
写 JSON、SQL 的痛苦,谁写谁知道:
java
// JDK 12 及之前:到处都是转义符
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30\n" +
"}";
// JDK 13+:所见即所得
String json = """
{
"name": "Alice",
"age": 30
}
""";语法规则:
- 以
"""开头,后面紧跟换行 - 结尾的
"""必须在单独一行 - 自动处理缩进对齐
java
// 注意缩进对齐
String query = """
SELECT id, name, email
FROM users
WHERE status = 'active'
ORDER BY created_at DESC
"""; // 前面空格会被自动去掉yield 关键字
箭头表达式可以直接返回值,但如果逻辑复杂需要多行语句呢?
java
// 单行箭头:直接返回
case "A" -> 1;
// 多行逻辑:需要 yield
int result = switch (grade) {
case "A" -> {
if (score >= 95) {
yield 100;
}
yield 90;
}
case "B" -> {
yield 80;
}
default -> {
System.out.println("Unknown grade");
yield 0;
}
};yield vs return:
return是方法返回yield是 switch 表达式返回值
JDK 14:Record 与 Pattern Matching 预览
instanceof 模式匹配(Preview → JDK 16 正式)
java
// JDK 13 及之前
if (obj instanceof String) {
String s = (String) obj; // 强制转换
System.out.println(s.length());
}
// JDK 14+:模式变量自动声明
if (obj instanceof String s) {
System.out.println(s.length()); // 直接用 s
}结合条件判断:
java
// JDK 16+
if (obj instanceof String s && s.length() > 5) {
System.out.println(s.toUpperCase());
}
// 短路求值保证安全Records 预览(Preview → JDK 16 正式)
POJO 类的样板代码,终于可以简化了:
java
// JDK 15 及之前
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 p = (Point) o;
return x == p.x && y == p.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
// JDK 16+:一行搞定
public record Point(int x, int y) {}自动生成的内容:
| 方法 | 说明 |
|---|---|
| 构造函数 | Point(int x, int y) |
| getter | x() 和 y()(不是 getX()) |
| equals() | 按值比较 |
| hashCode() | 基于字段 |
| toString() | 包含所有字段 |
JDK 15:ZGC 与 Sealed Classes
ZGC 生产可用
ZGC 从 JDK 11 实验阶段升级为生产可用:
bash
# JDK 15+:ZGC 生产可用
java -XX:+UseZGC -Xmx64g -jar application.jarSealed Classes 第二预览
密封类的概念在 JDK 15 二次预览(最终在 JDK 17 正式发布):
java
// JDK 17 正式语法
public sealed class Shape permits Circle, Rectangle, Square {}
// Circle 禁止进一步继承
public final class Circle extends Shape {}
// Rectangle 继续密封
public sealed class Rectangle extends Shape permits ColoredRectangle {}
// Square 打开继承
public non-sealed class Square extends Shape {}CharSequence.isEmpty()
java
// JDK 15+
CharSequence seq = "Hello";
seq.isEmpty(); // false
// 之前只能用 String 的 isEmpty()JDK 16:承前启后
JDK 16 是 JDK 17 LTS 的前身,引入多项正式版特性。
正式版特性
java
// 1. Records 正式版
public record User(String name, int age) {}
// 2. instanceof Pattern Matching 正式版
if (obj instanceof String s) {
System.out.println(s.length());
}
// 3. 方向性方法引用(::)
// JDK 16+ 允许更灵活的 lambda 和方法引用转换jpackage 打包工具
bash
# 创建平台原生安装包
jpackage --input path/to/input --main-jar myapp.jar \
--name MyApp --type exe # Windows
# --type dmg # macOS
# --type deb # LinuxStream.toList() 简化
java
// JDK 16+:toList() 返回不可变列表
List<String> list = Stream.of("a", "b", "c")
.filter(s -> s.length() > 1)
.toList(); // 比 .collect(Collectors.toList()) 简洁
// JDK 15 及之前
List<String> list = Stream.of("a", "b", "c")
.collect(Collectors.toList());版本对比一览
| 特性 | JDK 12 | JDK 13 | JDK 14 | JDK 15 | JDK 16 |
|---|---|---|---|---|---|
| Switch 表达式 | Preview | 正式 | |||
| yield 关键字 | Preview | 正式 | |||
| Text Block | Preview | 正式 | |||
| instanceof Pattern | Preview | 正式 | |||
| Records | Preview | 2nd Preview | 正式 | ||
| Sealed Classes | 2nd Preview | ||||
| ZGC 生产 | ✅ |
面试追问方向
Switch 表达式的「穷尽性检查」是什么?
- 编译器检查 switch 是否处理了所有可能值
- 如果有遗漏且没有 default,编译报错
Record 和普通类的区别是什么?
- Record 是隐式 final 的
- 不能声明实例字段(只能有组件)
- 没有 setX() 方法,Record 是不可变的
Text Block 的转义序列有哪些?
\n新行\t水平制表符\\反斜杠\s空格(保留行尾空格)"""三引号本身
Sealed Class 的 permits 子类有什么限制?
- 必须与父类在同一模块或包
- 必须是 final、sealed 或 non-sealed
留给你的思考题
Records 自动生成 equals()、hashCode() 和 toString() 方法。
但如果你的 Record 包含一个 List 字段,比如 Person(String name, List<String> hobbies),equals() 是如何比较 hobbies 的?
这涉及到浅比较 vs 深比较的问题——默认生成的 Record equals() 是浅比较还是深比较?
