Skip to content
<

Java 15 将文本块正式⁢化,新增了 Hid⁡den 隐藏类,并⁡引入了 Sealed 类作为预览特性。

正式特性

【必备】文本块

这可能是我最喜欢的特性之一了⁢,因为之前每次复制⁡多行文本到代码中,⁡都会给我转成这么一坨:

需要大量的字符串拼接、转义字⁢符,对于 HTML⁡、SQL 和 JS⁡ON 格式来说简直是噩梦了。

有了 Java 15 的文本块特性,多行字符串简直不要太爽!直接用三个引号 """ 括起来,就能以字符串本来的格式展示。

文本块会保持代码的缩进、而且内⁢部的引号不需要转义。

配合 String 的格式化⁢方法,就能轻松传入⁡参数生成复杂的字符⁡串模板:

文本块的使用示例

java
// HTML 模板  
String htmlTemplate = """  
    <!DOCTYPE html>  
    <html>  
    <head>  
        <title>%s</title>  
    </head>  
    <body>  
        <h1>欢迎 %s</h1>  
        <p>这是一个使用文本块的示例</p>  
    </body>  
    </html>  
    """;  
  
String html = htmlTemplate.formatted("我的网站", "鱼皮");  
  
// SQL 查询  
String sql = """  
    SELECT u.name, u.email, p.title  
    FROM users u  
    JOIN posts p ON u.id = p.user_id  
    WHERE u.status = 'ACTIVE'  
      AND p.published_at > ?  
    ORDER BY p.created_at DESC  
    LIMIT 10  
    """;  
  
// JSON 数据  
String json = """  
    {  
        "name": "张三",  
        "age": 30,  
        "skills": ["Java", "Python", "JavaScript"],  
        "address": {  
            "city": "北京",  
            "district": "朝阳区"  
        }  
    }  
    """;

文本块的处理方法

Java 15 为文本块提供了几个有用的处理方法:

java
String textBlock = """  
    第一行  
        缩进的第二行  
    第三行  
    """;  
  
// stripIndent() - 去除公共缩进  
String stripped = textBlock.stripIndent();  
  
// translateEscapes() - 处理转义序列  
String withEscapes = """  
    第一行\\n第二行\\t制表符  
    """;  
String translated = withEscapes.translateEscapes();  
  
// formatted() - 格式化文本块  
String template = """  
    用户名: %s  
    年龄: %d  
    邮箱: %s  
    """;  
String result = template.formatted("鱼皮", 25, "yupi@example.com");

【了解】Hidden 隐藏类

Java 15 引入了 Hidden 隐藏类特性,这是一个 专为框架和运行时环境设计 的底层机制,主要是为了优化 动态生成短期类(比如 Lambda 表达式、动态代理)的性能问题,普通开发者无需关心。

在 Lambda 表达式、AOP 动态代理、ORM 映射等场景中,框架会动态⁢生成代码载体(比如方法句柄、临时代理类),这些载体⁡需要关联类的元数据才能运行。如果生成频繁,传统类的⁡元数据会被类加载器追踪,需要等待类加载器卸载才能回收,导致元空间堆积和 GC 压力。

Hidden 类的特点是对其定义类加载器之外的所有代码都不⁢可见,由于不可发现且链接微弱,JVM ⁡垃圾回收器能够更高效地卸载隐藏类及其元⁡数据,从而防止短期类堆积对元空间造成压力,优化了需要动态生成大量类的性能。

Hidden 类的特点

  1. 不可发现性:不能通过反射 API 发现

  2. 弱链接:与类加载器的连接很弱,便于回收

  3. 访问控制:只能被定义它的类访问

  4. 性能优化:减少元空间压力

java
// 这是框架层面的 API,普通开发者不需要直接使用  
import java.lang.invoke.MethodHandles;  
  
public class HiddenClassExample {  
    public static void main(String[] args) throws Exception {  
        // 获取 Lookup 对象  
        MethodHandles.Lookup lookup = MethodHandles.lookup();  
          
        // 动态生成的字节码(简化示例)  
        byte[] classBytes = generateDynamicClass();  
          
        // 定义隐藏类  
        MethodHandles.Lookup hiddenLookup = lookup.defineHiddenClass(  
            classBytes,   
            true, // 是否初始化  
            MethodHandles.Lookup.ClassOption.NESTMATE  
        );  
          
        Class<?> hiddenClass = hiddenLookup.lookupClass();  
        System.out.println("隐藏类: " + hiddenClass.getName());  
          
        // 隐藏类不能通过 Class.forName() 找到  
        try {  
            Class.forName(hiddenClass.getName());  
        } catch (ClassNotFoundException e) {  
            System.out.println("隐藏类无法通过反射发现");  
        }  
    }  
      
    private static byte[] generateDynamicClass() {  
        // 实际场景中,这里会是动态生成的字节码  
        // 比如 Lambda 表达式、动态代理等  
        return new byte[0]; // 简化示例  
    }  
}

【了解】Edwards 曲线数字签名算法

Java 15 添加了对 EdDSA⁢(Edwards-cur⁡ve Digital S⁡ignature Algorithm)的支持:

java
import java.security.*;  
import java.security.spec.EdDSAParameterSpec;  
  
public class EdDSAExample {  
    public static void main(String[] args) throws Exception {  
        // 生成 Ed25519 密钥对  
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("Ed25519");  
        KeyPair keyPair = keyGen.generateKeyPair();  
          
        // 创建签名对象  
        Signature signature = Signature.getInstance("Ed25519");  
          
        // 签名  
        signature.initSign(keyPair.getPrivate());  
        byte[] message = "Hello EdDSA!".getBytes();  
        signature.update(message);  
        byte[] signatureBytes = signature.sign();  
          
        // 验证签名  
        signature.initVerify(keyPair.getPublic());  
        signature.update(message);  
        boolean isValid = signature.verify(signatureBytes);  
          
        System.out.println("签名验证: " + isValid);  
    }  
}

EdDSA 的优势:

  • 性能更好:比传统的 ECDSA 更快

  • 安全性高:抗侧信道攻击

  • 确定性:相同输入产生相同签名

【了解】ZGC 和 Shenandoah 正式化

Java 15 将 ZGC ⁢和 Shenand⁡oah 从实验特性⁡升级为正式特性:

bash
# ZGC 现在可以在生产环境使用  
java -XX:+UseZGC MyApp  
  
# Shenandoah 也正式可用  
java -XX:+UseShenandoahGC MyApp

这两个低延迟垃圾收集器的正式⁢化,为对延迟敏感的⁡应用提供了生产级别⁡的选择。

【了解】其他正式特性

重新实现传统 DatagramSocket API

Java 15 重新实现了 ⁢DatagramS⁡ocket 和 M⁡ulticastSocket:

java
// API 使用方式不变,但底层实现更现代化  
DatagramSocket socket = new DatagramSocket(8080);  
MulticastSocket multicastSocket = new MulticastSocket(8080);  
  
// 新实现的优势:  
// 1. 更好的可维护性  
// 2. 为 Project Loom 做准备  
// 3. 更好的性能表现

禁用和弃用偏向锁定

Java 15 默认禁用偏向锁定,并将其标记为弃用:

bash
# 偏向锁定默认禁用  
# 如果需要启用(不推荐)  
java -XX:+UseBiasedLocking MyApp

偏向锁定的问题:

  • 增加了 JVM 复杂性

  • 在现代多线程应用中收益有限

  • 与其他 JVM 特性存在冲突

删除 Nashorn JavaScript 引擎

Java 15 完全移除了 ⁢Nashorn J⁡avaScript⁡ 引擎:

java
// 这些 API 在 Java 15 中已被移除  
// ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");

推荐使用独立的 JavaSc⁢ript 引擎,如⁡ GraalVM ⁡的 JavaScript 实现。

预览特性

【了解】密封类(预览)

Java 15 引入了密封类⁢(Sealed C⁡lasses)作为⁡预览特性:

java
// 密封类只允许指定的类继承  
public sealed class Shape   
    permits Circle, Rectangle, Triangle {  
    // 基类代码  
}  
  
// 被允许的子类必须选择继承策略  
public final class Circle extends Shape {  
    // final: 不能再被继承  
}  
  
public sealed class Rectangle extends Shape   
    permits Square {  
    // sealed: 控制谁能继承  
}  
  
public non-sealed class Triangle extends Shape {  
    // non-sealed: 开放继承  
}

密封类的使用场景:

java
// 定义一个表达式的类型层次  
public sealed interface Expr   
    permits ConstantExpr, PlusExpr, TimesExpr {  
}  
  
public record ConstantExpr(int value) implements Expr {}  
public record PlusExpr(Expr left, Expr right) implements Expr {}  
public record TimesExpr(Expr left, Expr right) implements Expr {}  
  
// 使用密封类进行模式匹配(未来版本)  
public int eval(Expr expr) {  
    return switch (expr) {  
        case ConstantExpr(var value) -> value;  
        case PlusExpr(var left, var right) -> eval(left) + eval(right);  
        case TimesExpr(var left, var right) -> eval(left) * eval(right);  
        // 不需要 default,因为所有情况都覆盖了  
    };  
}

【了解】instanceof 的模式匹配(第二次预览)

Java 15 继续完善 i⁢nstanceof⁡ 的模式匹配:

java
// 改进的模式匹配  
public void processValue(Object obj) {  
    // 基本模式匹配  
    if (obj instanceof String str) {  
        System.out.println("字符串: " + str);  
    }  
      
    // 结合逻辑运算符  
    if (obj instanceof String str && str.length() > 5) {  
        System.out.println("长字符串: " + str);  
    }  
      
    // 在表达式中使用  
    String result = obj instanceof String str ? str.toUpperCase() : "非字符串";  
      
    // 否定形式  
    if (!(obj instanceof String str)) {  
        System.out.println("不是字符串");  
        return;  
    }  
    // 这里 str 可用,因为前面的否定条件  
    System.out.println("确认是字符串: " + str);  
}

【了解】Records(第二次预览)

Java 15 继续完善 Records:

java
// Records 支持泛型  
public record Pair<T, U>(T first, U second) {  
    // 静态方法  
    public static <T, U> Pair<T, U> of(T first, U second) {  
        return new Pair<>(first, second);  
    }  
}  
  
// 使用示例  
Pair<String, Integer> nameAge = Pair.of("张三", 25);  
System.out.println(nameAge.first()); // 张三  
System.out.println(nameAge.second()); // 25  
  
// Records 可以实现接口  
public interface Drawable {  
    void draw();  
}  
  
public record Point(int x, int y) implements Drawable {  
    @Override  
    public void draw() {  
        System.out.println("在 (" + x + ", " + y + ") 处绘制点");  
    }  
}

孵化器特性

【了解】外部内存访问 API(第二次孵化器)

Java 15 继续完善外部内⁢存访问 API:

java
import jdk.incubator.foreign.*;  
  
public class ForeignMemoryExample {  
    public static void main(String[] args) {  
        // 分配本地内存段  
        try (MemorySegment segment = MemorySegment.allocateNative(1024)) {  
            // 创建内存访问句柄  
            VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());  
              
            // 写入数据  
            intHandle.set(segment, 0L, 42);  
            intHandle.set(segment, 4L, 24);  
              
            // 读取数据  
            int value1 = (int) intHandle.get(segment, 0L);  
            int value2 = (int) intHandle.get(segment, 4L);  
              
            System.out.println("值1: " + value1 + ", 值2: " + value2);  
        }  
    }  
}

总结

Java 15 最重要的特性是文本块的正式化,这大大改善了多⁢行字符串的处理体验。Hidden 类的引⁡入虽然对普通开发者透明,但为框架性能优化⁡提供了重要支持。

ZGC 和 Shenandoah 的正式化标志着 J⁢ava 在低延迟垃圾收集方面的成熟⁡,为高性能应用提供了生产级别的选择⁡。密封类作为预览特性的引入,为类型安全和模式匹配奠定了基础。

虽然 Java 15 不是 LTS 版本,⁢但它的许多特性都为后续版本的⁡重要功能做了准备,特别是密封⁡类和 Records 在后续版本中得到了进一步完善。