JDK 15 新特性详解
JDK 15 于 2020年9月15日 正式发布,是 Java 半年发布节奏的第五个非 LTS 版本。JDK 15 最重要的贡献是 Text Blocks 正式标准化 、Sealed Classes(密封类) 预览引入、EdDSA 数字签名 、ZGC 和 Shenandoah GC 正式进入生产环境,同时移除了 Nashorn JavaScript 引擎、RMI Activation 等过时特性。
目录
- [Text Blocks 正式标准化](#Text Blocks 正式标准化)
- [Sealed Classes 密封类(预览)](#Sealed Classes 密封类(预览))
- [Records 记录类(第二次预览)](#Records 记录类(第二次预览))
- [Pattern Matching for instanceof(第二次预览)](#Pattern Matching for instanceof(第二次预览))
- [EdDSA 数字签名算法](#EdDSA 数字签名算法)
- [Hidden Classes 隐藏类](#Hidden Classes 隐藏类)
- [Foreign Linker API(孵化)](#Foreign Linker API(孵化))
- [ZGC 正式生产可用](#ZGC 正式生产可用)
- [Shenandoah GC 正式生产可用](#Shenandoah GC 正式生产可用)
- 移除的特性
- 其他重要变更
- [JDK 15 特性总览表](#JDK 15 特性总览表)
1. Text Blocks 正式标准化
1.1 概述
JEP 378 --- Text Blocks(文本块)在 JDK 13(预览)、JDK 14(第二次预览)后,终于在 JDK 15 正式标准化 。同时新增了 \ 行尾续行符和 \s 空格转义的正式支持。
标准化意味着:
- 无需
--enable-preview参数 - 可直接用于生产环境
- API 稳定,不会再有破坏性变更
1.2 代码案例
java
// ============ 1. 基础文本块 ============
String json = """
{
"name": "Alice",
"age": 25,
"city": "Beijing"
}
""";
String html = """
<!DOCTYPE html>
<html>
<head><title>示例</title></head>
<body>
<h1>Hello, Text Blocks!</h1>
</body>
</html>
""";
// ============ 2. 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 = 'ACTIVE'
AND o.created_at > '2024-01-01'
GROUP BY u.id, u.name, u.email
ORDER BY total_spent DESC
LIMIT 100
""";
// ============ 3. \s 空格转义 ============
// 保持行尾空格(某些格式要求)
String table = """
Name\s\s\s| Age | City
-----------|-----|----------
Alice\s\s\s| 25\s| Beijing
Bob\s\s\s\s\s| 30\s| Shanghai
""";
// ============ 4. \ 行尾续行 ============
// 忽略换行,拼接下一行
String singleLine = """
This is a very \
long line that \
spans multiple \
source lines""";
// 结果:"This is a very long line that spans multiple source lines"
// 混合使用
String mixed = """
Line 1
Line 2 续\
行内容
Line 4""";
// 结果:
// Line 1
// Line 2 续行内容
// Line 4
// ============ 5. 与 formatted() 配合 ============
String greeting = """
Dear %s,
Thank you for your order #%d.
Your order total is ¥%,.2f.
Best regards,
The Team
""".formatted("Alice", 12345, 999.99);
// ============ 6. 正则表达式 ============
// 多行正则更清晰
String emailRegex = """
^[a-zA-Z0-9._%+-]+
@
[a-zA-Z0-9.-]+
\\.
[a-zA-Z]{2,}$
""";
// 注意:需要 Pattern.COMMENTS 标志忽略空白
// ============ 7. 实际应用场景 ============
// JSON 构建
String buildUserJson(String name, int age, String email) {
return """
{
"name": "%s",
"age": %d,
"email": "%s",
"active": true,
"roles": ["USER"]
}
""".formatted(name, age, email);
}
// HTML 邮件模板
String buildEmail(String userName, String orderNo, BigDecimal total) {
return """
<html>
<body>
<h2>订单确认</h2>
<p>尊敬的 %s,</p>
<p>您的订单 <strong>%s</strong> 已确认。</p>
<p>订单金额: <strong>¥%.2f</strong></p>
<p>感谢您的购买!</p>
</body>
</html>
""".formatted(userName, orderNo, total);
}
// 多行日志
String logMessage = """
[ERROR] 数据库连接失败
主机: db-server-01
端口: 3306
原因: 连接超时
重试次数: 3
最后尝试: %s
""".formatted(java.time.LocalDateTime.now());
2. Sealed Classes 密封类(预览)
2.1 概述
JEP 360 --- Sealed Classes(密封类)以预览形式引入,是 JDK 15 最重要的新语言特性。密封类允许类或接口精确控制哪些类可以继承/实现它,增强了类型系统的安全性和表达能力。
核心关键字:
sealed--- 声明密封类/接口permits--- 指定允许的子类列表final--- 子类不可再继承sealed--- 子类继续密封non-sealed--- 子类开放继承(回到默认行为)
2.2 代码案例
java
// ============ 1. 基础密封类 ============
// 声明密封类,只允许指定的子类
public sealed class Shape permits Circle, Rectangle, Triangle {
// 公共方法
public abstract double area();
public abstract double perimeter();
}
// 子类必须是 final、sealed 或 non-sealed 之一
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public double perimeter() {
return 2 * Math.PI * radius;
}
}
public final class Rectangle extends Shape {
private final double width;
private final double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
@Override
public double perimeter() {
return 2 * (width + height);
}
}
public final class Triangle extends Shape {
private final double a, b, c; // 三边
public Triangle(double a, double b, double c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public double area() {
double s = (a + b + c) / 2;
return Math.sqrt(s * (s - a) * (s - b) * (s - c));
}
@Override
public double perimeter() {
return a + b + c;
}
}
// 编译错误:不允许的子类
// public class Pentagon extends Shape { } // 编译错误!
// ============ 2. 密封接口 ============
public sealed interface Expr permits Constant, Add, Multiply {
double eval();
}
public record Constant(double value) implements Expr {
@Override
public double eval() {
return value;
}
}
public record Add(Expr left, Expr right) implements Expr {
@Override
public double eval() {
return left.eval() + right.eval();
}
}
public record Multiply(Expr left, Expr right) implements Expr {
@Override
public double eval() {
return left.eval() * right.eval();
}
}
// 使用
Expr expr = new Multiply(
new Add(new Constant(2), new Constant(3)),
new Constant(4)
);
System.out.println(expr.eval()); // (2 + 3) * 4 = 20.0
// ============ 3. 三种子类修饰符 ============
sealed class Animal permits Dog, Cat, Bird {
abstract void speak();
}
// final --- 不可再继承
final class Dog extends Animal {
@Override
void speak() { System.out.println("Woof!"); }
}
// sealed --- 继续密封,控制更下一层
sealed class Cat extends Animal permits Lion, Tiger {
@Override
void speak() { System.out.println("Meow!"); }
}
// non-sealed --- 开放继承,回到默认行为
non-sealed class Bird extends Animal {
@Override
void speak() { System.out.println("Tweet!"); }
}
// Cat 的子类也必须遵守密封约束
final class Lion extends Cat {
@Override
void speak() { System.out.println("Roar!"); }
}
final class Tiger extends Cat {
@Override
void speak() { System.out.println("Grrr!"); }
}
// Bird 是 non-sealed,任何类都可以继承
class Sparrow extends Bird { } // OK
class Eagle extends Bird { } // OK
// ============ 4. 配合 switch 模式匹配(预览,JDK 17+) ============
// 密封类 + switch 模式匹配 = 穷举检查
// 编译器知道所有可能的子类,可以检查是否覆盖完整
// JDK 17+ 语法(JDK 15 中需要手动 instanceof 检查)
String describe(Shape shape) {
if (shape instanceof Circle c) {
return "圆形,半径: " + c.radius;
} else if (shape instanceof Rectangle r) {
return "矩形,宽: " + r.width + ",高: " + r.height;
} else if (shape instanceof Triangle t) {
return "三角形,边长: " + t.a + ", " + t.b + ", " + t.c;
}
throw new IllegalStateException("Unknown shape");
}
// ============ 5. 密封类与 Record 配合 ============
// 密封接口 + Record 实现 = 代数数据类型(ADT)
public sealed interface JsonValue
permits JsonString, JsonNumber, JsonBoolean, JsonNull, JsonArray, JsonObject {
}
public record JsonString(String value) implements JsonValue {}
public record JsonNumber(double value) implements JsonValue {}
public record JsonBoolean(boolean value) implements JsonValue {}
public record JsonNull() implements JsonValue {}
public record JsonArray(List<JsonValue> elements) implements JsonValue {}
public record JsonObject(Map<String, JsonValue> properties) implements JsonValue {}
// 使用
JsonValue json = new JsonObject(Map.of(
"name", new JsonString("Alice"),
"age", new JsonNumber(25),
"active", new JsonBoolean(true),
"address", new JsonNull()
));
// ============ 6. 实际应用场景 ============
// 6.1 领域建模 --- 有限状态
sealed interface PaymentResult permits PaymentSuccess, PaymentFailed, PaymentPending {
String message();
}
record PaymentSuccess(String transactionId, BigDecimal amount) implements PaymentResult {
@Override
public String message() {
return "支付成功,交易号: " + transactionId;
}
}
record PaymentFailed(String errorCode, String reason) implements PaymentResult {
@Override
public String message() {
return "支付失败: " + reason;
}
}
record PaymentPending(String pendingId) implements PaymentResult {
@Override
public String message() {
return "支付处理中,请稍后查询";
}
}
// 处理结果
void handlePayment(PaymentResult result) {
if (result instanceof PaymentSuccess success) {
System.out.println("处理成功订单: " + success.transactionId());
} else if (result instanceof PaymentFailed failed) {
System.out.println("处理失败: " + failed.reason());
} else if (result instanceof PaymentPending pending) {
System.out.println("等待支付确认: " + pending.pendingId());
}
}
// 6.2 表达式树
sealed interface Tree<T> permits Leaf, Branch {
T fold(BinaryOperator<T> combiner, T identity);
}
record Leaf<T>(T value) implements Tree<T> {
@Override
public T fold(BinaryOperator<T> combiner, T identity) {
return value;
}
}
record Branch<T>(Tree<T> left, Tree<T> right) implements Tree<T> {
@Override
public T fold(BinaryOperator<T> combiner, T identity) {
return combiner.apply(left.fold(combiner, identity),
right.fold(combiner, identity));
}
}
// ============ 7. 编译与运行 ============
// Sealed classes 是预览特性
javac --enable-preview --release 15 App.java
java --enable-preview -cp . App
3. Records 记录类(第二次预览)
3.1 概述
JEP 384 --- Records 在 JDK 14(首次预览)后,JDK 15 进行了第二次预览。主要改进包括:
- Records 可以实现局部(local)接口 --- 在方法内部定义 Record
- 改进约束 --- 更清晰的规范说明
3.2 代码案例
java
// ============ 1. 基础 Record(与 JDK 14 相同) ============
public record Point(double x, double y) {
public double distanceTo(Point other) {
return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(y - other.y, 2));
}
}
Point p1 = new Point(0, 0);
Point p2 = new Point(3, 4);
System.out.println(p1.distanceTo(p2)); // 5.0
// ============ 2. 局部 Record(JDK 15 新增) ============
// 在方法内部定义 Record
void processData(List<String> data) {
// 局部 Record
record Result(String name, int count, double average) {}
Map<String, Long> counts = data.stream()
.collect(Collectors.groupingBy(s -> s, Collectors.counting()));
List<Result> results = counts.entrySet().stream()
.map(e -> new Result(e.getKey(), e.getValue().intValue(), 0.0))
.collect(Collectors.toList());
results.forEach(r -> System.out.println(r));
}
// ============ 3. Record 与密封类配合 ============
sealed interface Shape permits Circle, Rectangle {
double area();
}
record Circle(double radius) implements Shape {
@Override
public double area() {
return Math.PI * radius * radius;
}
}
record Rectangle(double width, double height) implements Shape {
@Override
public double area() {
return width * height;
}
}
// ============ 4. Record 的紧凑构造器增强 ============
public record DateRange(LocalDate start, LocalDate end) {
// 紧凑构造器中进行验证
public DateRange {
Objects.requireNonNull(start, "start must not be null");
Objects.requireNonNull(end, "end must not be null");
if (start.isAfter(end)) {
throw new IllegalArgumentException("start must not be after end");
}
}
// 便捷方法
public long days() {
return ChronoUnit.DAYS.between(start, end);
}
public boolean contains(LocalDate date) {
return !date.isBefore(start) && !date.isAfter(end);
}
}
DateRange range = new DateRange(LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31));
System.out.println(range.days()); // 365
System.out.println(range.contains(LocalDate.of(2024, 6, 15))); // true
4. Pattern Matching for instanceof(第二次预览)
4.1 概述
JEP 375 --- Pattern Matching for instanceof 在 JDK 14(首次预览)后,JDK 15 进行了第二次预览。主要改进是模式变量的作用域规则更加清晰。
4.2 代码案例
java
// ============ 1. 基础模式匹配 ============
Object obj = "Hello World";
// 传统写法
if (obj instanceof String) {
String str = (String) obj;
System.out.println(str.length());
}
// 模式匹配
if (obj instanceof String str) {
System.out.println(str.length());
}
// ============ 2. 作用域规则改进(JDK 15) ============
// JDK 15 明确了模式变量的作用域:
// 1. 在 if 的 true 分支中可用
// 2. 在 && 组合的右侧可用
// 3. 在取反后的后续代码中可用(if 没有 else 时)
Object value = "test";
// 取反后,模式变量在后续可用
if (!(value instanceof String str)) {
throw new IllegalArgumentException("Not a string");
}
// str 在这里可用(JDK 15 明确允许)
System.out.println("长度: " + str.length());
// ============ 3. 与逻辑运算配合 ============
// && 组合
if (obj instanceof String str && str.length() > 5) {
System.out.println("长字符串: " + str);
}
// || 组合时,模式变量在右侧不可用
// if (obj instanceof String str || str.isEmpty()) // 编译错误
// ============ 4. equals 方法优化 ============
class Person {
String name;
int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person person)) return false; // 模式匹配
return age == person.age && Objects.equals(name, person.name);
}
}
// ============ 5. 实际应用场景 ============
// 多类型处理
void process(Object obj) {
if (obj instanceof Integer i) {
System.out.println("整数: " + (i * 2));
} else if (obj instanceof String s) {
System.out.println("字符串: " + s.toUpperCase());
} else if (obj instanceof List<?> list) {
System.out.println("列表大小: " + list.size());
}
}
// 异常处理
try {
riskyOperation();
} 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;
}
}
5. EdDSA 数字签名算法
5.1 概述
JEP 339 --- EdDSA(Edwards-Curve Digital Signature Algorithm)数字签名算法支持。EdDSA 是一种基于椭圆曲线的数字签名算法,比传统的 RSA/DSA 更安全、性能更好。JDK 15 新增了对 Ed25519 和 Ed448 曲线的支持。
优势:
- 性能优于 RSA(签名和验证速度更快)
- 安全性更高(抗侧信道攻击)
- 签名更短
- 被 TLS 1.3 推荐
5.2 代码案例
java
// ============ 1. Ed25519 签名 ============
import java.security.*;
// 生成 Ed25519 密钥对
KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
KeyPair keyPair = kpg.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 签名
byte[] message = "Hello, EdDSA!".getBytes(StandardCharsets.UTF_8);
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(privateKey);
sig.update(message);
byte[] signature = sig.sign();
System.out.println("签名长度: " + signature.length + " bytes"); // 64 bytes
// 验证
Signature verifier = Signature.getInstance("Ed25519");
verifier.initVerify(publicKey);
verifier.update(message);
boolean isValid = verifier.verify(signature);
System.out.println("签名验证: " + isValid); // true
// 篡改消息后验证
byte[] tamperedMessage = "Hello, EdDSA!".getBytes(StandardCharsets.UTF_8);
tamperedMessage[0] = 'X'; // 篡改
verifier.initVerify(publicKey);
verifier.update(tamperedMessage);
boolean isTampered = verifier.verify(signature);
System.out.println("篡改后验证: " + isTampered); // false
// ============ 2. Ed448 签名 ============
// Ed448 提供更高的安全级别(224位安全性 vs Ed25519 的 128位)
KeyPairGenerator kpg448 = KeyPairGenerator.getInstance("Ed448");
KeyPair keyPair448 = kpg448.generateKeyPair();
Signature sig448 = Signature.getInstance("Ed448");
sig448.initSign(keyPair448.getPrivate());
sig448.update(message);
byte[] signature448 = sig448.sign();
System.out.println("Ed448 签名长度: " + signature448.length + " bytes"); // 114 bytes
// ============ 3. 与 RSA 对比 ============
// RSA 2048
KeyPairGenerator rsaKpg = KeyPairGenerator.getInstance("RSA");
rsaKpg.initialize(2048);
KeyPair rsaKeyPair = rsaKpg.generateKeyPair();
Signature rsaSig = Signature.getInstance("SHA256withRSA");
rsaSig.initSign(rsaKeyPair.getPrivate());
rsaSig.update(message);
byte[] rsaSignature = rsaSig.sign();
System.out.println("RSA 2048 签名长度: " + rsaSignature.length + " bytes"); // 256 bytes
System.out.println("Ed25519 签名长度: " + signature.length + " bytes"); // 64 bytes
// EdDSA 签名长度固定且远短于 RSA
// ============ 4. 实际应用场景 ============
// 4.1 JWT 签名(EdDSA)
String jwtHeader = """
{
"alg": "EdDSA",
"typ": "JWT"
}
""";
// 4.2 TLS 证书签名
// Ed25519 被 TLS 1.3 推荐用于证书签名
// 4.3 区块链/加密货币
// 许多区块链项目使用 Ed25519(如 Solana、Cardano)
// 4.4 API 请求签名
class ApiRequestSigner {
private final Signature signature;
public ApiRequestSigner(PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException {
this.signature = Signature.getInstance("Ed25519");
this.signature.initSign(privateKey);
}
public byte[] sign(String requestBody) throws SignatureException {
signature.update(requestBody.getBytes(StandardCharsets.UTF_8));
return signature.sign();
}
}
// 4.5 文件完整性校验
class FileSigner {
public static byte[] signFile(Path filePath, PrivateKey privateKey)
throws Exception {
Signature sig = Signature.getInstance("Ed25519");
sig.initSign(privateKey);
try (InputStream is = Files.newInputStream(filePath)) {
byte[] buffer = new byte[8192];
int read;
while ((read = is.read(buffer)) != -1) {
sig.update(buffer, 0, read);
}
}
return sig.sign();
}
public static boolean verifyFile(Path filePath, byte[] signature, PublicKey publicKey)
throws Exception {
Signature sig = Signature.getInstance("Ed25519");
sig.initVerify(publicKey);
try (InputStream is = Files.newInputStream(filePath)) {
byte[] buffer = new byte[8192];
int read;
while ((read = is.read(buffer)) != -1) {
sig.update(buffer, 0, read);
}
}
return sig.verify(signature);
}
}
6. Hidden Classes 隐藏类
6.1 概述
JEP 371 --- Hidden Classes(隐藏类)。隐藏类是一种特殊的类,对普通的类加载不可见,主要用于框架(如 Spring、Hibernate、Jackson)在运行时动态生成的类。隐藏类可以被 GC 回收,不会造成内存泄漏。
特点:
- 不可被普通代码发现(
Class.forName()找不到) - 可以独立于宿主类被卸载
- 访问控制更灵活
6.2 代码案例
java
// ============ 1. 创建隐藏类 ============
import java.lang.invoke.*;
// 通过 MethodHandles.Lookup 创建隐藏类
byte[] classBytes = generateClassBytes(); // 假设这是字节码
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class<?> hiddenClass = lookup.defineHiddenClass(classBytes, true)
.lookupClass();
// 隐藏类不可通过普通方式发现
try {
Class.forName(hiddenClass.getName()); // 抛出 ClassNotFoundException
} catch (ClassNotFoundException e) {
System.out.println("隐藏类无法通过 Class.forName 发现");
}
// 但可以直接使用
Object instance = hiddenClass.getDeclaredConstructor().newInstance();
System.out.println("隐藏类实例: " + instance);
// ============ 2. 隐藏类与框架 ============
// Spring AOP 动态代理
// Hibernate 实体代理
// Jackson 序列化/反序列化生成的辅助类
// 这些框架都可以使用隐藏类来避免类加载器污染
// ============ 3. 隐藏类的优势 ============
// 1. 不可发现 --- 不会与用户类冲突
// 2. 可独立卸载 --- 框架卸载时,生成的类也可以被 GC
// 3. 访问控制 --- 可以更精细地控制访问权限
// ============ 4. 实际应用场景 ============
// 动态代理生成器
class ProxyGenerator {
public static Class<?> generateProxy(Class<?> targetInterface) throws Exception {
// 使用 ASM 或 ByteBuddy 生成字节码
byte[] bytecode = generateProxyBytecode(targetInterface);
// 创建隐藏类
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(
targetInterface, MethodHandles.lookup());
return lookup.defineHiddenClass(bytecode, true).lookupClass();
}
private static byte[] generateProxyBytecode(Class<?> targetInterface) {
// 实际实现使用 ASM/ByteBuddy
return new byte[0]; // 占位
}
}
7. Foreign Linker API(孵化)
7.1 概述
JEP 383 --- Foreign Linker API(外部链接器 API)以孵化形式引入,替代了 JDK 14 的 Foreign Memory Access API。该 API 允许 Java 程序调用本地库(native libraries),无需 JNI。
7.2 代码案例
java
// ============ 1. 调用 C 标准库函数 ============
import jdk.incubator.foreign.*;
import static jdk.incubator.foreign.FunctionDescriptor.*;
import static jdk.incubator.foreign.CLinker.*;
// 获取 C 链接器
CLinker linker = CLinker.getInstance();
// 查找 C 标准库中的 strlen 函数
SymbolLookup stdlib = SymbolLookup.loaderLookup();
MemoryAddress strlenAddr = stdlib.lookup("strlen").orElseThrow();
// 创建方法句柄
MethodHandle strlen = linker.downcallHandle(
strlenAddr,
MethodType.methodType(long.class, MemoryAddress.class),
FunctionDescriptor.of(C_LONG, C_POINTER)
);
// 调用
try (MemorySegment str = CLinker.toCString("Hello, World!")) {
long length = (long) strlen.invoke(str.baseAddress());
System.out.println("字符串长度: " + length); // 13
}
// ============ 2. 调用自定义 C 库 ============
// 假设有 C 库:libmath.so / libmath.dylib / math.dll
// int add(int a, int b) { return a + b; }
// 加载本地库
System.loadLibrary("math");
// 查找 add 函数
SymbolLookup mathLib = SymbolLookup.loaderLookup();
MemoryAddress addAddr = mathLib.lookup("add").orElseThrow();
// 创建方法句柄
MethodHandle add = linker.downcallHandle(
addAddr,
MethodType.methodType(int.class, int.class, int.class),
FunctionDescriptor.of(C_INT, C_INT, C_INT)
);
// 调用
int result = (int) add.invoke(3, 5);
System.out.println("3 + 5 = " + result); // 8
// ============ 3. 操作本地内存 ============
// 分配本地内存
try (MemorySegment segment = MemorySegment.allocateNative(1024)) {
MemoryAddress base = segment.baseAddress();
// 通过 VarHandle 读写
VarHandle intHandle = MemoryLayout.structLayout(C_INT).varHandle(int.class);
intHandle.set(base, 42);
int value = (int) intHandle.get(base);
System.out.println("值: " + value); // 42
}
// ============ 4. 编译与运行 ============
// 孵化 API 需要额外参数
// javac --add-modules jdk.incubator.foreign App.java
// java --add-modules jdk.incubator.foreign App
8. ZGC 正式生产可用
8.1 概述
JEP 377 --- ZGC(Z Garbage Collector)从实验性特性升级为生产可用 。JDK 15 中不再需要 -XX:+UnlockExperimentalVMOptions 来启用 ZGC。
8.2 使用方式
bash
# ============ 1. 启用 ZGC(JDK 15+) ============
# 不再需要 UnlockExperimentalVMOptions
java -XX:+UseZGC -jar app.jar
# ============ 2. 配合其他参数 ============
java -XX:+UseZGC \
-XX:MaxRAMPercentage=75.0 \
-XX:+UseZGCUncommit \
-jar app.jar
# ============ 3. ZGC 特性 ============
# - 暂停时间 < 10ms(与堆大小无关)
# - 支持 TB 级堆
# - 自动内存归还(JDK 13+)
# - 支持 Linux、macOS、Windows(JDK 14+)
# ============ 4. 适用场景 ============
# - 大堆应用(> 4GB)
# - 低延迟要求(< 10ms)
# - 容器化部署
# - 长时间运行的服务
9. Shenandoah GC 正式生产可用
9.1 概述
JEP 379 --- Shenandoah GC 从实验性特性升级为生产可用。Shenandoah 是 Red Hat 开发的低延迟垃圾收集器,与 ZGC 类似但实现不同。
9.2 使用方式
bash
# ============ 1. 启用 Shenandoah GC(JDK 15+) ============
# 不再需要 UnlockExperimentalVMOptions
java -XX:+UseShenandoahGC -jar app.jar
# ============ 2. ZGC vs Shenandoah ============
# | 特性 | ZGC | Shenandoah |
# |---------------|------------------|------------------|
# | 暂停时间 | < 10ms | < 10ms |
# | 最大堆 | TB 级 | TB 级 |
# | 压缩方式 | 并发压缩 | 并发压缩 |
# | 平台支持 | Linux/Mac/Win | Linux/Mac/Win |
# | 来源 | Oracle | Red Hat |
# ============ 3. 选择建议 ============
# - 追求极致低延迟 → ZGC
# - 需要 Red Hat 支持 → Shenandoah
# - 两者性能接近,选择团队熟悉的即可
10. 移除的特性
10.1 移除列表
JEP 372 --- 移除 Nashorn JavaScript 引擎、RMI Activation 等过时特性。
bash
# ============ 1. Nashorn JavaScript 引擎移除 ============
# JDK 15 之前可以使用
jjs script.js # Nashorn 命令行工具
// 或在 Java 中调用 JavaScript
ScriptEngine engine = new ScriptEngineManager()
.getEngineByName("nashorn");
engine.eval("print('Hello from JS');");
# JDK 15+ 已移除
# 替代方案:
# 1. GraalVM JavaScript(推荐)
# 2. GraalJS
# 3. 其他 JS 引擎(如 Rhino)
# ============ 2. RMI Activation 移除 ============
# java.rmi.activation 包已移除
# RMI Activation 是 Java 远程方法调用的一个过时特性
# 替代方案:
# 1. 使用 Spring Remoting
# 2. 使用 gRPC
# 3. 使用 REST API
# ============ 3. 其他移除 ============
# - EC 密钥对的密钥大小验证(已移除过时的验证逻辑)
# - 部分 AWT/Swing 旧 API
11. 其他重要变更
11.1 废弃和标记
java
// ============ 1. 偏向锁标记为废弃 ============
// -XX:+UseBiasedLocking 标记为废弃
// 未来版本可能移除偏向锁
// ============ 2. DatagramSocket API 重写完成 ============
// JDK 13 重写了 Socket API
// JDK 15 完成了 DatagramSocket 的重写
// 底层使用 NIO 实现,API 不变
// ============ 3. 新的安全特性 ============
// 改进了密钥派生函数(KDF)支持
// 增强了 TLS 1.3 的实现
12. JDK 15 特性总览表
| 序号 | 特性 | JEP | 类型 | 重要性 |
|---|---|---|---|---|
| 1 | Text Blocks 正式标准化 | JEP 378 | 语言 | ★★★★★ |
| 2 | Sealed Classes 密封类(预览) | JEP 360 | 语言 | ★★★★★ |
| 3 | Records 记录类(第二次预览) | JEP 384 | 语言 | ★★★★☆ |
| 4 | Pattern Matching instanceof(第二次预览) | JEP 375 | 语言 | ★★★★☆ |
| 5 | EdDSA 数字签名算法 | JEP 339 | 安全 | ★★★★☆ |
| 6 | Hidden Classes 隐藏类 | JEP 371 | 平台 | ★★★☆☆ |
| 7 | Foreign Linker API(孵化) | JEP 383 | API | ★★★★☆ |
| 8 | ZGC 正式生产可用 | JEP 377 | GC | ★★★★★ |
| 9 | Shenandoah GC 正式生产可用 | JEP 379 | GC | ★★★★☆ |
| 10 | Nashorn/RMI Activation 移除 | JEP 372 | 清理 | ★★★☆☆ |
附录:预览特性状态追踪
| 特性 | JDK 12 | JDK 13 | JDK 14 | JDK 15 | JDK 16 | JDK 17 |
|---|---|---|---|---|---|---|
| Switch 表达式 | 预览 | 第二次预览 | 正式 | --- | --- | --- |
| Text Blocks | --- | 预览 | 第二次预览 | 正式 | --- | --- |
| Records | --- | --- | 预览 | 第二次预览 | 正式 | --- |
| Pattern Matching instanceof | --- | --- | 预览 | 第二次预览 | 正式 | --- |
| Sealed Classes | --- | --- | --- | 预览 | 第二次预览 | 正式 |
总结 :JDK 15 是一个承前启后的重要版本。Text Blocks 正式标准化 让多行字符串成为生产可用的利器,Sealed Classes 引入了代数数据类型的概念,ZGC 和 Shenandoah GC 正式生产可用 标志着 Java 低延迟 GC 的成熟,EdDSA 为现代密码学提供了原生支持。JDK 15 为 JDK 17(下一个 LTS)的最终特性组合奠定了坚实基础。