Java 15 将文本块正式化,新增了 Hidden 隐藏类,并引入了 Sealed 类作为预览特性。
正式特性
【必备】文本块
这可能是我最喜欢的特性之一了,因为之前每次复制多行文本到代码中,都会给我转成这么一坨:

需要大量的字符串拼接、转义字符,对于 HTML、SQL 和 JSON 格式来说简直是噩梦了。
有了 Java 15 的文本块特性,多行字符串简直不要太爽!直接用三个引号 """ 括起来,就能以字符串本来的格式展示。

文本块会保持代码的缩进、而且内部的引号不需要转义。
配合 String 的格式化方法,就能轻松传入参数生成复杂的字符串模板:

文本块的使用示例
// 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 为文本块提供了几个有用的处理方法:
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 类的特点
不可发现性:不能通过反射 API 发现
弱链接:与类加载器的连接很弱,便于回收
访问控制:只能被定义它的类访问
性能优化:减少元空间压力
// 这是框架层面的 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-curve Digital Signature Algorithm)的支持:
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 和 Shenandoah 从实验特性升级为正式特性:
# ZGC 现在可以在生产环境使用
java -XX:+UseZGC MyApp
# Shenandoah 也正式可用
java -XX:+UseShenandoahGC MyApp这两个低延迟垃圾收集器的正式化,为对延迟敏感的应用提供了生产级别的选择。
【了解】其他正式特性
重新实现传统 DatagramSocket API
Java 15 重新实现了 DatagramSocket 和 MulticastSocket:
// API 使用方式不变,但底层实现更现代化
DatagramSocket socket = new DatagramSocket(8080);
MulticastSocket multicastSocket = new MulticastSocket(8080);
// 新实现的优势:
// 1. 更好的可维护性
// 2. 为 Project Loom 做准备
// 3. 更好的性能表现禁用和弃用偏向锁定
Java 15 默认禁用偏向锁定,并将其标记为弃用:
# 偏向锁定默认禁用
# 如果需要启用(不推荐)
java -XX:+UseBiasedLocking MyApp偏向锁定的问题:
增加了 JVM 复杂性
在现代多线程应用中收益有限
与其他 JVM 特性存在冲突
删除 Nashorn JavaScript 引擎
Java 15 完全移除了 Nashorn JavaScript 引擎:
// 这些 API 在 Java 15 中已被移除
// ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");推荐使用独立的 JavaScript 引擎,如 GraalVM 的 JavaScript 实现。
预览特性
【了解】密封类(预览)
Java 15 引入了密封类(Sealed Classes)作为预览特性:
// 密封类只允许指定的类继承
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: 开放继承
}密封类的使用场景:
// 定义一个表达式的类型层次
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 继续完善 instanceof 的模式匹配:
// 改进的模式匹配
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:
// 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:
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 的正式化标志着 Java 在低延迟垃圾收集方面的成熟,为高性能应用提供了生产级别的选择。密封类作为预览特性的引入,为类型安全和模式匹配奠定了基础。
虽然 Java 15 不是 LTS 版本,但它的许多特性都为后续版本的重要功能做了准备,特别是密封类和 Records 在后续版本中得到了进一步完善。