JDK 14 新特性详解
JDK 14 新特性详解
JDK 14 于 2020年3月17日 正式发布,是 Java 半年发布节奏的第四个非 LTS 版本。JDK 14 引入了多个重磅特性:Records(记录类) 预览、Pattern Matching for instanceof 预览、Switch 表达式正式标准化、Helpful NullPointerExceptions(更友好的空指针异常信息)、JFR 事件流 等,是继 JDK 12/13 之后又一个特性丰富的版本。
目录
- Records 记录类(预览)
- Pattern Matching for instanceof(预览)
- Switch 表达式正式标准化
- Text Blocks 文本块(第二次预览)
- Helpful NullPointerExceptions 空指针异常增强
- Foreign Memory Access API(孵化)
- JFR 事件流
- Packaging Tool 打包工具(孵化)
- ZGC 跨平台支持
- CMS GC 移除
- 其他重要变更
- JDK 14 特性总览表
1. Records 记录类(预览)
1.1 概述
JEP 395 — Records(记录类)是 JDK 14 最重要的语言特性,以预览形式引入。Records 提供了一种紧凑的语法来声明不可变数据载体类(data carrier classes),编译器自动生成构造器、getter、equals()、hashCode()、toString() 等方法。
核心特点:
- 不可变(所有字段隐式
final) - 自动生成构造器、访问器(注意:不是
getXxx(),而是xxx())、equals()、hashCode()、toString() - 不能继承其他类(隐式继承
java.lang.Record) - 可以实现接口
- 可以声明静态字段和方法
- 可以声明实例方法(覆盖自动生成的方法)
1.2 代码案例
// ============ 1. 基础 Record 定义 ============
// 传统写法(需要大量样板代码)
public final class Person {
private final String name;
private final int age;
private final String email;
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public String name() { return name; }
public int age() { return age; }
public String email() { return email; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name) &&
Objects.equals(email, person.email);
}
@Override
public int hashCode() {
return Objects.hash(name, age, email);
}
@Override
public String toString() {
return "Person[name=" + name + ", age=" + age + ", email=" + email + "]";
}
}
// Record 写法(一行搞定)
public record PersonRecord(String name, int age, String email) {}
// 使用
PersonRecord person = new PersonRecord("Alice", 25, "alice@example.com");
System.out.println(person.name()); // "Alice"(注意:不是 getName())
System.out.println(person.age()); // 25
System.out.println(person.email()); // "alice@example.com"
System.out.println(person); // PersonRecord[name=Alice, age=25, email=alice@example.com]
// ============ 2. equals 和 hashCode 自动实现 ============
PersonRecord p1 = new PersonRecord("Alice", 25, "alice@example.com");
PersonRecord p2 = new PersonRecord("Alice", 25, "alice@example.com");
PersonRecord p3 = new PersonRecord("Bob", 30, "bob@example.com");
System.out.println(p1.equals(p2)); // true(值相等)
System.out.println(p1.equals(p3)); // false
System.out.println(p1.hashCode() == p2.hashCode()); // true
// ============ 3. 紧凑构造器(Compact Constructor) ============
// 可以在紧凑构造器中添加参数校验
public record Range(int min, int max) {
// 紧凑构造器(无参数列表)
public Range {
if (min > max) {
throw new IllegalArgumentException("min > max: " + min + " > " + max);
}
}
}
Range valid = new Range(1, 10); // OK
Range invalid = new Range(10, 1); // 抛出 IllegalArgumentException
// ============ 4. 自定义方法 ============
public record Point(double x, double y) {
// 自定义实例方法
public double distanceTo(Point other) {
double dx = this.x - other.x;
double dy = this.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
}
public double distanceToOrigin() {
return Math.sqrt(x * x + y * y);
}
// 覆盖自动生成的 toString
@Override
public String toString() {
return String.format("(%f, %f)", x, y);
}
}
Point p = new Point(3.0, 4.0);
System.out.println(p.distanceToOrigin()); // 5.0
System.out.println(p.distanceTo(new Point(6.0, 8.0))); // 5.0
// ============ 5. 静态工厂方法 ============
public record Money(BigDecimal amount, Currency currency) {
// 静态工厂方法
public static Money of(double amount, String currencyCode) {
return new Money(BigDecimal.valueOf(amount), Currency.getInstance(currencyCode));
}
public static Money zero(String currencyCode) {
return new Money(BigDecimal.ZERO, Currency.getInstance(currencyCode));
}
// 静态方法
public static Money max(Money a, Money b) {
if (!a.currency().equals(b.currency())) {
throw new IllegalArgumentException("Currency mismatch");
}
return a.amount().compareTo(b.amount()) >= 0 ? a : b;
}
}
Money m = Money.of(99.99, "CNY");
System.out.println(m); // Money[amount=99.99, currency=CNY]
// ============ 6. 实现接口 ============
public interface Printable {
String toFormattedString();
}
public record User(String name, String email, int age) implements Printable {
@Override
public String toFormattedString() {
return String.format("用户名: %s, 邮箱: %s, 年龄: %d", name, email, age);
}
}
User user = new User("Alice", "alice@example.com", 25);
System.out.println(user.toFormattedString());
// "用户名: Alice, 邮箱: alice@example.com, 年龄: 25"
// ============ 7. Record 与集合配合 ============
List<PersonRecord> people = List.of(
new PersonRecord("Alice", 25, "alice@example.com"),
new PersonRecord("Bob", 30, "bob@example.com"),
new PersonRecord("Charlie", 25, "charlie@example.com")
);
// 按年龄分组
Map<Integer, List<PersonRecord>> byAge = people.stream()
.collect(Collectors.groupingBy(PersonRecord::age));
// {25=[PersonRecord[name=Alice,...], PersonRecord[name=Charlie,...]],
// 30=[PersonRecord[name=Bob,...]]}
// 作为 Map 的 key(因为 hashCode/equals 已实现)
Map<PersonRecord, String> personRoles = new HashMap<>();
personRoles.put(p1, "管理员");
personRoles.put(p2, "管理员"); // p1.equals(p2),所以会覆盖
System.out.println(personRoles.size()); // 1
// ============ 8. 嵌套 Record ============
public record Address(String city, String street, String zipCode) {}
public record Company(String name, Address address) {}
Company company = new Company(
"Tech Corp",
new Address("Beijing", "Zhongguancun Street", "100080")
);
System.out.println(company.address().city()); // "Beijing"
// ============ 9. Record 与序列化 ============
// Record 实现了 java.io.Serializable(如果组件类型都可序列化)
// 序列化格式与组件类型一致
// ============ 10. 实际应用场景 ============
// DTO(数据传输对象)
public record UserDTO(Long id, String username, String email, LocalDateTime createdAt) {}
// 数据库查询结果映射
public record OrderSummary(Long orderId, String customerName, BigDecimal total, String status) {}
// 配置对象
public record DatabaseConfig(String url, String username, String password, int maxPoolSize) {}
// API 响应
public record ApiResponse<T>(int code, String message, T data) {
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
}
ApiResponse<String> response = ApiResponse.success("Hello");
System.out.println(response); // ApiResponse[code=200, message=success, data=Hello]
1.3 编译与运行
# Record 是预览特性
javac --enable-preview --release 14 App.java
java --enable-preview -cp . App
2. Pattern Matching for instanceof(预览)
2.1 概述
JEP 305 — Pattern Matching for instanceof(instanceof 模式匹配)以预览形式引入。传统的 instanceof 检查后需要显式类型转换,模式匹配可以在类型检查的同时自动绑定变量,省去冗余的类型转换代码。
2.2 代码案例
// ============ 1. 传统写法 vs 模式匹配 ============
// 传统写法
Object obj = "Hello World";
if (obj instanceof String) {
String str = (String) obj; // 显式类型转换
System.out.println(str.length());
}
// 模式匹配写法(JDK 14)
if (obj instanceof String str) { // 类型检查 + 变量绑定一步完成
System.out.println(str.length()); // 直接使用 str
}
// ============ 2. 结合逻辑运算 ============
// 模式变量在条件表达式中可用
Object obj2 = "Hello";
// && 组合
if (obj2 instanceof String str && str.length() > 3) {
System.out.println("字符串长度大于3: " + str);
}
// 注意:|| 组合时,模式变量在右侧不可用
// if (obj instanceof String str || str.length() > 3) // 编译错误!
// ============ 3. 取反场景 ============
// 传统写法
if (!(obj instanceof String str)) {
// str 在这里不可用
throw new IllegalArgumentException("Not a string");
}
// str 在这里可用(JDK 14 预览)
System.out.println(str.length());
// ============ 4. 多重类型判断 ============
Object value = 42;
// 传统写法
if (value instanceof Integer) {
int i = (Integer) value;
System.out.println("Integer: " + i);
} else if (value instanceof Long) {
long l = (Long) value;
System.out.println("Long: " + l);
} else if (value instanceof String) {
String s = (String) value;
System.out.println("String: " + s);
}
// 模式匹配写法
if (value instanceof Integer i) {
System.out.println("Integer: " + i);
} else if (value instanceof Long l) {
System.out.println("Long: " + l);
} else if (value instanceof String s) {
System.out.println("String: " + s);
}
// ============ 5. equals 方法优化 ============
// 传统 equals
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false; // 传统:需要后续强转
Person person = (Person) o;
return age == person.age && name.equals(person.name);
}
// 模式匹配 equals
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person person)) return false; // 模式匹配
return age == person.age && name.equals(person.name);
}
// ============ 6. 实际应用场景 ============
// 处理多态对象
void process(Object obj) {
if (obj instanceof String s) {
System.out.println("字符串: " + s.toUpperCase());
} else if (obj instanceof Integer i) {
System.out.println("整数: " + (i * 2));
} else if (obj instanceof List<?> list) {
System.out.println("列表大小: " + list.size());
} else if (obj instanceof Map<?, ?> map) {
System.out.println("Map 大小: " + map.size());
} else if (obj == null) {
System.out.println("null");
} else {
System.out.println("未知类型: " + obj.getClass().getSimpleName());
}
}
// JSON 值处理
Object jsonValue = Map.of("name", "Alice", "age", 25);
if (jsonValue instanceof Map<?, ?> map) {
map.forEach((key, value) -> {
System.out.println(key + ": " + value);
});
}
// 异常处理
try {
// 某些操作
} catch (Exception e) {
if (e instanceof SQLException sqlEx) {
System.out.println("SQL 错误码: " + sqlEx.getErrorCode());
} else if (e instanceof IOException ioEx) {
System.out.println("IO 错误: " + ioEx.getMessage());
} else {
throw e;
}
}
2.3 作用域规则
// ============ 模式变量的作用域 ============
// 1. 模式变量在 instanceof 为 true 的分支中可用
if (obj instanceof String str) {
System.out.println(str); // OK
}
// System.out.println(str); // 编译错误:str 不在作用域内
// 2. 与 && 组合时,模式变量在右侧可用
if (obj instanceof String str && str.length() > 0) {
System.out.println(str); // OK
}
// 3. 与 || 组合时,模式变量在右侧不可用
// if (obj instanceof String str || str.isEmpty()) // 编译错误
// 4. 在条件表达式的 else 分支中不可用
if (obj instanceof String str) {
System.out.println(str); // OK
} else {
// System.out.println(str); // 编译错误
}
3. Switch 表达式正式标准化
3.1 概述
JEP 361 — Switch Expressions 在 JDK 12(预览)、JDK 13(第二次预览)后,终于在 JDK 14 正式标准化。这意味着可以直接在生产环境中使用,无需 --enable-preview 参数。
3.2 代码案例
// ============ 1. 基础 Switch 表达式 ============
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
// 箭头语法(无 fall-through)
int numLetters = switch (Day.MONDAY) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case WEDNESDAY, THURSDAY -> 9;
case SATURDAY -> 8;
};
// ============ 2. yield 关键字 ============
// 冒号形式 + yield
String describe = switch (Day.MONDAY) {
case MONDAY:
case FRIDAY:
case SUNDAY:
yield "休息或工作";
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
yield "工作日";
case SATURDAY:
yield "周末";
default:
yield "未知";
};
// ============ 3. 箭头形式 + 代码块 + yield ============
String result = switch ("JAVA") {
case "JAVA" -> {
String desc = "Java 编程语言";
yield desc;
}
case "PYTHON" -> {
yield "Python 编程语言";
}
default -> "未知语言";
};
// ============ 4. 实际应用场景 ============
// HTTP 状态码
String httpStatus(int code) {
return switch (code) {
case 200 -> "OK";
case 201 -> "Created";
case 204 -> "No Content";
case 400 -> "Bad Request";
case 401 -> "Unauthorized";
case 403 -> "Forbidden";
case 404 -> "Not Found";
case 500 -> "Internal Server Error";
default -> "Unknown";
};
}
// 类型判断(配合 instanceof 模式匹配)
String format(Object obj) {
return switch (obj) {
case null -> "null";
case String s -> "String: " + s;
case Integer i -> "Integer: " + i;
case Long l -> "Long: " + l;
default -> "Object: " + obj.toString();
};
}
// 注意:上面的 switch on Object 需要 JDK 17+ 的模式匹配
// JDK 14 中 switch 只能用于基本类型、String、enum
// 月份所属季度
int getQuarter(int month) {
return switch (month) {
case 1, 2, 3 -> 1;
case 4, 5, 6 -> 2;
case 7, 8, 9 -> 3;
case 10, 11, 12 -> 4;
default -> throw new IllegalArgumentException("Invalid month: " + month);
};
}
// 颜色名称
String colorName(String hex) {
return switch (hex.toUpperCase()) {
case "#FF0000", "#RED" -> "红色";
case "#00FF00", "#GREEN" -> "绿色";
case "#0000FF", "#BLUE" -> "蓝色";
case "#FFFFFF", "#WHITE" -> "白色";
case "#000000", "#BLACK" -> "黑色";
default -> "未知颜色: " + hex;
};
}
4. Text Blocks 文本块(第二次预览)
4.1 概述
JEP 368 — Text Blocks 在 JDK 13(首次预览)后,JDK 14 进行了第二次预览,新增了三个重要能力:
\行尾续行符 — 忽略换行,拼接下一行(JDK 13 已有)- 新增
\s转义 — 表示单个空格(JDK 13 已有) - 新增转义规则 — 更灵活的空白处理
4.2 代码案例
// ============ 1. 基础文本块 ============
String json = """
{
"name": "Alice",
"age": 25
}
""";
// ============ 2. \s 转义(保持行尾空格) ============
// 某些格式要求行尾有空格
String table = """
Name\s\s| Age | City\s\s\s
--------|-----|----------
Alice\s\s| 25\s| Beijing\s
Bob\s\s\s| 30\s| Shanghai
""";
// \s 确保行尾空格被保留
// ============ 3. \ 行尾续行 ============
// 不使用续行符
String withNewline = """
Hello
World""";
// "Hello\nWorld"
// 使用续行符(忽略换行)
String withoutNewline = """
Hello \
World""";
// "Hello World"(中间没有换行)
// 多行续行
String longLine = """
This is a very \
long line that \
spans multiple \
source lines""";
// "This is a very long line that spans multiple source lines"
// ============ 4. 与 formatted() 配合 ============
String html = """
<html>
<body>
<h1>%s</h1>
<p>Welcome, %s! You have %d messages.</p>
</body>
</html>
""".formatted("消息中心", "Alice", 5);
// ============ 5. 缩进控制技巧 ============
// 结尾 """ 在最左边 → 无去缩进
String noIndent = """
Line A
Line B
Line C
""";
// 结尾 """ 与内容对齐 → 按内容最小缩进去缩进
String aligned = """
Line A
Line B
Line C
""";
// 结果:每行前面没有多余缩进
// 混合缩进
String mixed = """
Line A
Line B(缩进2空格)
Line C(缩进4空格)
""";
// 相对基准的缩进被保留
// ============ 6. SQL 查询 ============
String sql = """
SELECT u.id, u.name, u.email,
COUNT(o.id) AS order_count,
SUM(o.total) AS total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = ?
GROUP BY u.id, u.name, u.email
HAVING COUNT(o.id) > ?
ORDER BY total_spent DESC
""";
// ============ 7. 实际应用场景 ============
// JSON 模板
String buildJson(String name, int age) {
return """
{
"name": "%s",
"age": %d,
"timestamp": "%s"
}
""".formatted(name, age, java.time.LocalDateTime.now());
}
// HTML 邮件模板
String buildEmail(String userName, String orderNo) {
return """
<html>
<body>
<h2>订单确认</h2>
<p>尊敬的 %s,</p>
<p>您的订单 <strong>%s</strong> 已确认。</p>
<p>感谢您的购买!</p>
</body>
</html>
""".formatted(userName, orderNo);
}
5. Helpful NullPointerExceptions 空指针异常增强
5.1 概述
JEP 358 — Helpful NullPointerExceptions(更友好的空指针异常信息)。JDK 14 改进了 NPE 的异常消息,精确指出哪个变量为 null以及哪个操作导致了 NPE,极大提升了调试效率。
改进前:NullPointerException(无有用信息)
改进后:Cannot invoke "String.length()" because "str" is null
5.2 代码案例
// ============ 1. 改进前的 NPE 消息 ============
// JDK 13 及之前
String str = null;
str.length();
// 异常消息:null(没有任何有用信息)
// ============ 2. 改进后的 NPE 消息 ============
// JDK 14+
String str = null;
str.length();
// 异常消息:Cannot invoke "String.length()" because "str" is null
// ============ 3. 链式调用场景 ============
class User {
Address address;
}
class Address {
String city;
}
User user = new User();
user.address = null;
// JDK 13 及之前
String city = user.address.city.toUpperCase();
// 异常消息:null
// 不知道是 user 为 null、address 为 null、还是 city 为 null
// JDK 14+
String city = user.address.city.toUpperCase();
// 异常消息:Cannot read field "city" because "user.address" is null
// 精确定位:是 address 为 null
// ============ 4. 各种场景的 NPE 消息 ============
// 4.1 方法调用
Object obj = null;
obj.toString();
// Cannot invoke "Object.toString()" because "obj" is null
// 4.2 字段访问
class Container {
String value;
}
Container container = new Container();
container.value = null;
int len = container.value.length();
// Cannot invoke "String.length()" because "container.value" is null
// 4.3 数组访问
int[] arr = null;
int val = arr[0];
// Cannot load from object array because "arr" is null
// 4.4 数组元素方法调用
String[] strings = null;
int len = strings[0].length();
// Cannot load from object array because "strings" is null
// 4.5 嵌套字段
class Outer {
Inner inner;
}
class Inner {
String name;
}
Outer outer = new Outer();
outer.inner = null;
String name = outer.inner.name;
// Cannot read field "name" because "outer.inner" is null
// ============ 5. 启用方式 ============
// JDK 14 默认启用
// 如果需要关闭(不推荐):
// java -XX:-ShowCodeDetailsInExceptionMessages -jar app.jar
// ============ 6. 实际影响 ============
// 改进前:调试 NPE 需要
// 1. 看堆栈找到行号
// 2. 打开源码看那一行
// 3. 逐个检查哪些变量可能为 null
// 4. 加日志或断点排查
// 改进后:
// 1. 看异常消息就知道哪个变量为 null
// 2. 直接定位问题根因
// 生产环境示例
public class UserService {
public String getCity(User user) {
return user.getAddress().getCity().toUpperCase();
}
}
// JDK 13: NullPointerException(需要逐步排查)
// JDK 14+: Cannot invoke "Address.getCity()" because the return value of
// "User.getAddress()" is null
// 一目了然:getAddress() 返回了 null
6. Foreign Memory Access API(孵化)
6.1 概述
JEP 370 — Foreign Memory Access API(外部内存访问 API)以孵化形式引入。该 API 允许 Java 程序安全高效地访问堆外内存(native memory),用于与本地库交互、处理高性能计算场景。
核心类:
MemorySegment— 内存段(连续内存区域)MemoryAddress— 内存地址MemoryLayout— 内存布局描述SegmentAllocator— 内存分配器
6.2 代码案例
// ============ 1. 分配堆外内存 ============
import jdk.incubator.foreign.*;
import static jdk.incubator.foreign.MemoryLayout.*;
// 分配 100 字节的堆外内存
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
// 使用完毕后自动释放(AutoCloseable)
MemoryAddress base = segment.baseAddress();
System.out.println("内存地址: " + base);
}
// ============ 2. 使用 MemoryLayout 描述内存布局 ============
// 描述一个 C 结构体:
// struct Point {
// int x;
// int y;
// };
MemoryLayout pointLayout = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")
);
// 分配匹配该布局的内存
try (MemorySegment segment = MemorySegment.allocateNative(pointLayout)) {
// 访问字段
VarHandle xHandle = pointLayout.varHandle(int.class, MemoryLayout.PathElement.groupElement("x"));
VarHandle yHandle = pointLayout.varHandle(int.class, MemoryLayout.PathElement.groupElement("y"));
MemoryAddress base = segment.baseAddress();
xHandle.set(base, 10); // x = 10
yHandle.set(base, 20); // y = 20
System.out.println("x = " + xHandle.get(base)); // 10
System.out.println("y = " + yHandle.get(base)); // 20
}
// ============ 3. 数组操作 ============
// 分配 int 数组(10 个元素)
MemoryLayout arrayLayout = MemoryLayout.sequenceLayout(10, ValueLayout.JAVA_INT);
try (MemorySegment segment = MemorySegment.allocateNative(arrayLayout)) {
VarHandle elemHandle = arrayLayout.varHandle(int.class,
MemoryLayout.PathElement.sequenceElement());
MemoryAddress base = segment.baseAddress();
// 写入数据
for (int i = 0; i < 10; i++) {
elemHandle.set(base, (long) i, i * i); // arr[i] = i*i
}
// 读取数据
for (int i = 0; i < 10; i++) {
System.out.print(elemHandle.get(base, (long) i) + " ");
}
// 0 1 4 9 16 25 36 49 64 81
}
// ============ 4. 与 ByteBuffer 互转 ============
// ByteBuffer → MemorySegment
ByteBuffer buffer = ByteBuffer.allocateDirect(64);
try (MemorySegment segment = MemorySegment.ofByteBuffer(buffer)) {
// 通过 MemorySegment 操作
segment.asSlice(0, 4).fill((byte) 42);
}
// ============ 5. 实际应用场景 ============
// 与 C 库交互(通过 JNI/JNA 的替代方案)
// 调用 C 函数:int add(int a, int b)
// 通过 FunctionDescriptor 和 MethodHandle 调用
// 高性能计算
// 直接操作堆外内存,避免 Java 堆内存 GC 开销
// 网络编程
// 零拷贝(zero-copy)场景,直接操作 DMA 缓冲区
// ============ 6. 编译与运行 ============
// 孵化 API 需要额外参数
// javac --add-modules jdk.incubator.foreign App.java
// java --add-modules jdk.incubator.foreign App
7. JFR 事件流
7.1 概述
JEP 349 — JFR(Java Flight Recorder)事件流。JDK 14 允许以流式方式实时消费 JFR 事件,而无需先导出为文件再分析。这使得实时监控和诊断成为可能。
7.2 代码案例
// ============ 1. 基础事件流 ============
import jdk.jfr.consumer.*;
import java.nio.file.Path;
import java.time.Duration;
// 创建事件流(实时)
try (RecordingStream stream = new RecordingStream()) {
// 设置事件过滤
stream.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
stream.enable("jdk.GCHeapSummary").withPeriod(Duration.ofSeconds(5));
// 注册事件处理器
stream.onEvent("jdk.CPULoad", event -> {
double jvmCpuLoad = event.getFloat("jvmUser") + event.getFloat("jvmSystem");
System.out.printf("JVM CPU 使用率: %.1f%%%n", jvmCpuLoad * 100);
});
stream.onEvent("jdk.GCHeapSummary", event -> {
long heapUsed = event.getLong("heapUsed");
long heapTotal = event.getLong("heapTotal");
System.out.printf("堆内存: %d MB / %d MB%n",
heapUsed / 1024 / 1024, heapTotal / 1024 / 1024);
});
// 设置超时(运行 30 秒后关闭)
stream.onEvent("jdk.CPULoad", event -> {}); // 触发流启动
stream.start();
}
// ============ 2. 从 JFR 文件读取事件 ============
try (RecordingStream stream = new RecordingStream(Path.of("recording.jfr"))) {
stream.onEvent("jdk.ObjectAllocationInNewTLAB", event -> {
String className = event.getClass("objectClass").getName();
long allocationSize = event.getLong("allocationSize");
System.out.printf("分配: %s (%d bytes)%n", className, allocationSize);
});
stream.start();
}
// ============ 3. 自定义事件 + 流消费 ============
import jdk.jfr.*;
@Name("com.example.HttpRequest")
@Label("HTTP Request")
@Description("Tracks HTTP request processing")
public class HttpRequestEvent extends Event {
@Label("URL")
String url;
@Label("Method")
String method;
@Label("Status Code")
int statusCode;
@Label("Duration (ms)")
long durationMs;
}
// 发布事件
HttpRequestEvent event = new HttpRequestEvent();
event.url = "/api/users";
event.method = "GET";
event.statusCode = 200;
event.durationMs = 45;
event.commit();
// 流式消费自定义事件
try (RecordingStream stream = new RecordingStream()) {
stream.enable("com.example.HttpRequest");
stream.onEvent("com.example.HttpRequest", e -> {
System.out.printf("[%s] %s %s → %d (%dms)%n",
e.getStartTime(),
e.getString("method"),
e.getString("url"),
e.getInt("statusCode"),
e.getLong("durationMs"));
});
stream.start();
}
// ============ 4. 实时监控面板 ============
// 模拟一个简单的实时监控
try (RecordingStream stream = new RecordingStream()) {
stream.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
stream.enable("jdk.ThreadStatistics").withPeriod(Duration.ofSeconds(2));
stream.onEvent("jdk.CPULoad", event -> {
double machineTotal = event.getFloat("machineTotal");
double jvmTotal = event.getFloat("jvmUser") + event.getFloat("jvmSystem");
System.out.printf("[CPU] 系统: %.1f%% | JVM: %.1f%%%n",
machineTotal * 100, jvmTotal * 100);
});
stream.onEvent("jdk.ThreadStatistics", event -> {
long threadCount = event.getLong("activeThreadCount");
long daemonCount = event.getLong("daemonThreadCount");
System.out.printf("[线程] 活跃: %d | 守护: %d%n", threadCount, daemonCount);
});
stream.start();
}
// ============ 5. 编译与运行 ============
// JFR 事件流不需要 --enable-preview
// javac App.java
// java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder App
8. Packaging Tool 打包工具(孵化)
8.1 概述
JEP 343 — Packaging Tool(打包工具)以孵化形式引入,用于创建平台原生安装包(Linux 的 deb/rpm、macOS 的 pkg/dmg、Windows 的 msi/exe)。
8.2 使用方式
# ============ 1. 基础打包命令 ============
# 创建 Linux deb 包
jpackage --name MyApp \
--input target/libs \
--main-jar myapp.jar \
--main-class com.example.Main \
--type deb \
--dest output/
# 创建 Windows msi 包
jpackage --name MyApp \
--input target/libs \
--main-jar myapp.jar \
--main-class com.example.Main \
--type msi \
--dest output/
# 创建 macOS pkg 包
jpackage --name MyApp \
--input target/libs \
--main-jar myapp.jar \
--main-class com.example.Main \
--type pkg \
--dest output/
# ============ 2. 常用参数 ============
jpackage --name "My Application" \
--app-version 1.0.0 \
--vendor "My Company" \
--description "My awesome application" \
--input target/libs \
--main-jar myapp.jar \
--main-class com.example.Main \
--java-options "-Xmx512m" \
--java-options "-Dfile.encoding=UTF-8" \
--icon resources/icon.ico \
--dest output/
# ============ 3. 创建带运行时的安装包 ============
# 包含 JRE(用户无需安装 Java)
jpackage --name MyApp \
--runtime-image target/runtime-image \
--main-jar myapp.jar \
--main-class com.example.Main \
--type exe \
--dest output/
# ============ 4. 配置文件方式 ============
# 创建配置文件 myapp.properties
# type=deb
# name=MyApp
# app-version=1.0.0
# vendor=MyCompany
# input=target/libs
# main-jar=myapp.jar
# main-class=com.example.Main
# java-options=-Xmx512m
# dest=output/
# 使用配置文件
jpackage @myapp.properties
9. ZGC 跨平台支持
9.1 概述
JDK 14 将 ZGC(Z Garbage Collector)扩展到了 macOS(JEP 364)和 Windows(JEP 365)平台。此前 ZGC 仅支持 Linux。
9.2 使用方式
# ============ 1. 启用 ZGC ============
# macOS
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar app.jar
# Windows
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar app.jar
# ============ 2. 配合容器使用 ============
# Docker(Linux 环境,ZGC 完整支持)
docker run -m 4g java \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseZGC \
-XX:MaxRAMPercentage=75.0 \
-jar app.jar
# ============ 3. ZGC 在各平台的状态 ============
# Linux: 实验性(JDK 11)→ 生产可用(JDK 15)
# macOS: 实验性(JDK 14)
# Windows: 实验性(JDK 14)
10. CMS GC 移除
10.1 概述
JEP 363 — 移除 Concurrent Mark Sweep(CMS)垃圾收集器。CMS 在 JDK 9 已被标记为废弃(deprecated),JDK 14 正式将其移除。
10.2 影响
# ============ 1. 移除前 ============
# JDK 13 及之前可以使用 CMS
java -XX:+UseConcMarkSweepGC -jar app.jar
# ============ 2. 移除后 ============
# JDK 14+ 使用 CMS 会报错
java -XX:+UseConcMarkSweepGC -jar app.jar
# 错误:Unrecognized VM option 'UseConcMarkSweepGC'
# ============ 3. 迁移建议 ============
# CMS → G1(推荐,JDK 9+ 默认)
java -XX:+UseG1GC -jar app.jar
# CMS → ZGC(低延迟场景)
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar app.jar
# CMS → Shenandoah(低延迟,OpenJDK 提供)
java -XX:+UseShenandoahGC -jar app.jar
# ============ 4. 各 GC 对比 ============
# | GC | 特点 | 适用场景 |
# |-------------|-------------------|-----------------|
# | Serial GC | 单线程,最简单 | 小堆、容器 |
# | Parallel GC | 多线程,高吞吐 | 批处理 |
# | G1 GC | 分区,可预测暂停 | 通用(默认) |
# | ZGC | 超低延迟(<10ms) | 大堆、低延迟 |
# | Shenandoah | 低延迟,并发压缩 | 低延迟 |
11. 其他重要变更
11.1 废弃的 API 和特性
// ============ 1. 移除的特性 ============
// 1. CMS GC 移除(JEP 363)
// 2. ParallelScavenge + Serial GC 组合的废弃通知
// 3. 移除了部分旧的 AWT/Swing 相关 API
// ============ 2. 废弃的 API ============
// Biased Locking(偏向锁)开始标记为废弃
// -XX:+UseBiasedLocking 在未来版本可能移除
// ============ 3. 新的启动方式 ============
// source-file 单文件运行增强
// 可以直接运行包含 shebang 的 Java 文件
// #!/usr/bin/java --source 14
// public class HelloWorld {
// public static void main(String[] args) {
// System.out.println("Hello from Java 14!");
// }
// }
// 在 Linux/macOS 上可以直接执行
// chmod +x HelloWorld.java
// ./HelloWorld.java
11.2 macOS/AArch64 移植准备
JDK 14 为 Apple Silicon(M1/M2)芯片做了前期准备工作,虽然正式的 macOS/AArch64 移植在 JDK 17 完成,但 JDK 14 开始了相关基础设施搭建。
12. JDK 14 特性总览表
| 序号 | 特性 | JEP | 类型 | 重要性 |
|---|---|---|---|---|
| 1 | Records 记录类(预览) | JEP 395 | 语言 | ★★★★★ |
| 2 | Pattern Matching for instanceof(预览) | JEP 305 | 语言 | ★★★★★ |
| 3 | Switch 表达式正式标准化 | JEP 361 | 语言 | ★★★★★ |
| 4 | Text Blocks 文本块(第二次预览) | JEP 368 | 语言 | ★★★★☆ |
| 5 | Helpful NullPointerExceptions | JEP 358 | 调试 | ★★★★★ |
| 6 | Foreign Memory Access API(孵化) | JEP 370 | API | ★★★★☆ |
| 7 | JFR 事件流 | JEP 349 | 监控 | ★★★★☆ |
| 8 | Packaging Tool 打包工具(孵化) | JEP 343 | 工具 | ★★★☆☆ |
| 9 | ZGC on macOS | JEP 364 | GC | ★★★☆☆ |
| 10 | ZGC on Windows | JEP 365 | GC | ★★★☆☆ |
| 11 | CMS GC 移除 | JEP 363 | GC | ★★★★☆ |
附录:预览特性状态追踪
| 特性 | JDK 12 | JDK 13 | JDK 14 | JDK 15 | JDK 16 | JDK 17 |
|---|---|---|---|---|---|---|
| Switch 表达式 | 预览 | 第二次预览 | 正式 | — | — | — |
| Text Blocks | — | 预览 | 第二次预览 | 正式 | — | — |
| Records | — | — | 预览 | 第二次预览 | 正式 | — |
| Pattern Matching instanceof | — | — | 预览 | 第二次预览 | 正式 | — |
| Foreign Memory Access | — | — | 孵化 | 孵化 | 孵化 | 孵化 |
| Packaging Tool | — | — | 孵化 | 孵化 | 正式 | — |
总结:JDK 14 是一个特性密集的版本。Records 为数据类提供了极简语法,Pattern Matching for instanceof 减少了类型转换的样板代码,Switch 表达式正式标准化 标志着这一重要语言特性的成熟,Helpful NullPointerExceptions 极大提升了调试体验,JFR 事件流 让实时监控成为可能。JDK 14 为后续 LTS 版本(JDK 17)的诸多正式特性奠定了基础。
更多推荐


所有评论(0)