JDK 15 新特性详解

JDK 15 新特性详解

JDK 15 于 2020年9月15日 正式发布,是 Java 半年发布节奏的第五个非 LTS 版本。JDK 15 最重要的贡献是 Text Blocks 正式标准化Sealed Classes(密封类) 预览引入、EdDSA 数字签名ZGC 和 Shenandoah GC 正式进入生产环境,同时移除了 Nashorn JavaScript 引擎、RMI Activation 等过时特性。


目录

  1. [Text Blocks 正式标准化](#Text Blocks 正式标准化)
  2. [Sealed Classes 密封类(预览)](#Sealed Classes 密封类(预览))
  3. [Records 记录类(第二次预览)](#Records 记录类(第二次预览))
  4. [Pattern Matching for instanceof(第二次预览)](#Pattern Matching for instanceof(第二次预览))
  5. [EdDSA 数字签名算法](#EdDSA 数字签名算法)
  6. [Hidden Classes 隐藏类](#Hidden Classes 隐藏类)
  7. [Foreign Linker API(孵化)](#Foreign Linker API(孵化))
  8. [ZGC 正式生产可用](#ZGC 正式生产可用)
  9. [Shenandoah GC 正式生产可用](#Shenandoah GC 正式生产可用)
  10. 移除的特性
  11. 其他重要变更
  12. [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 进行了第二次预览。主要改进包括:

  1. Records 可以实现局部(local)接口 --- 在方法内部定义 Record
  2. 改进约束 --- 更清晰的规范说明

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 新增了对 Ed25519Ed448 曲线的支持。

优势

  • 性能优于 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)的最终特性组合奠定了坚实基础。

相关推荐
L-影6 小时前
springboot启动流程
java·spring boot·spring
ch.ju6 小时前
Java Programming Chapter 4——lambda expression
java·开发语言
布朗克1686 小时前
Go 入门到精通-18-文件操作
java·数据库·golang
罗超驿7 小时前
10.Java对象比较完全指南:从equals到Comparable与Comparator
java·开发语言
LiaoWL1237 小时前
【JUC合集-01】JUC并发编程复习总结
java
Tisfy8 小时前
LeetCode 1979.找出数组的最大公约数:模拟(附手动gcd)
java·数学·算法·leetcode·题解·最大公约数
糖果店的幽灵8 小时前
【langgraph 从入门到精通graphApi 篇】节点与边 —— 图的骨架
java·数据库·人工智能·redis·langgraph
CHANG_THE_WORLD8 小时前
7.C++标准多线程实现 TCP通信
java·c++·tcp/ip
chen_ke_hao8 小时前
K8s 高可用集群部署
java·docker·kubernetes