JDK 13 新特性详解
JDK 13 新特性详解
JDK 13 于 2019年9月17日 正式发布,是 JDK 9 之后延续半年发布节奏的第三个非 LTS 版本。JDK 13 虽然不包含像 JDK 12 那样引入预览特性的震撼力,但在 Text Blocks(文本块) 预览、Socket API 重写、ZGC 内存回收、动态 CDS 归档 等方面带来了实质性的改进,为后续 LTS 版本(JDK 17/21)奠定了重要基础。
目录
- 文本块 Text Blocks(预览)
- Switch 表达式增强(第二次预览)
- 动态 CDS 归档
- ZGC 未使用内存回收
- Socket API 重写
- String.formatted() 方法
- FileSystems 新工厂方法
- NIO ByteBuffer 增强
- JVM Constants API 更新
- SSL/TLS 增强
- javax.swing 增强
- JDK 13 特性总览表
1. 文本块 Text Blocks(预览)
1.1 概述
JEP 355 — Text Blocks(文本块)是 JDK 13 最重要的语言特性,以预览形式引入。文本块是一种多行字符串字面量,可以省去大量转义字符和字符串拼接,让嵌入在 Java 代码中的 HTML、JSON、SQL、XML 等片段变得可读性极强。
语法:使用 """ 三个双引号作为界定符,内容自动保留换行和缩进。
"""
第一行
第二行
第三行
"""
核心规则:
- 开头
"""必须在行尾(后面只能跟空白字符和换行符) - 结尾
"""决定缩进基准(内容会相对此基准进行去缩进) - 内容中的
"不需要转义,""也不需要,只有连续三个"""才需要转义 - 支持
\n、\t、\s等转义,新增\行尾续行符(忽略换行)
1.2 代码案例
// ============ 1. 基础文本块 ============
// 传统写法(难以阅读)
String json_old = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 25,\n" +
" \"city\": \"Beijing\"\n" +
"}";
// 文本块写法(清晰直观)
String json = """
{
"name": "Alice",
"age": 25,
"city": "Beijing"
}
""";
System.out.println(json);
// 输出:
// {
// "name": "Alice",
// "age": 25,
// "city": "Beijing"
// }
// ============ 2. HTML 片段 ============
String html = """
<!DOCTYPE html>
<html>
<head>
<title>示例页面</title>
</head>
<body>
<h1>Hello, Text Blocks!</h1>
<p>这是一个多行字符串的示例。</p>
</body>
</html>
""";
// ============ 3. SQL 查询 ============
String sql = """
SELECT u.id, u.name, u.email, o.order_id, o.total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'ACTIVE'
AND o.created_at > '2024-01-01'
ORDER BY o.total DESC
LIMIT 100
""";
// ============ 4. 缩进规则 ============
// 结尾 """ 的位置决定了去缩进的基准
// 内容中每行相对于 """ 的缩进会被保留
String indent1 = """
Line A
Line B(缩进2个空格相对基准)
Line C
""";
// 结果:
// Line A
// Line B(缩进2个空格相对基准)
// Line C
// 如果结尾 """ 放在内容中间,会影响去缩进
String indent2 = """
Line A
Line B
Line C""";
// 结果相同(""" 在行尾或内容末尾效果一样)
// ============ 5. 转义字符 ============
// \ 行尾续行符(忽略换行,拼接下一行)
String noNewline = """
Hello \
World""";
// 结果:"Hello World"(中间没有换行)
// \s 表示空格(用于保持行尾空格)
String trailingSpace = """
Line A\s
Line B""";
// "Line A " 末尾有一个空格(\s 转义为空格)
// \n 显式换行
String explicitNewline = """
Line A\n
Line B""";
// "Line A\n\nLine B"(多了一个空行)
// 双引号不需要转义
String quotes = """
She said "Hello" to me.
""";
// 正常输出:She said "Hello" to me.
// 三个连续双引号需要转义
String tripleQuote = """
这里是三个连续双引号:\"""
""";
// ============ 6. 文本块与 String 方法结合 ============
String template = """
SELECT * FROM %s WHERE id = %d
""";
String result = String.format(template, "users", 42);
// "SELECT * FROM users WHERE id = 42\n"
// 配合 formatted() 方法(JDK 13 同时新增)
String result2 = """
SELECT * FROM %s WHERE id = %d
""".formatted("users", 42);
// ============ 7. 实际应用场景 ============
// JSON 构建
String buildUserJson(String name, int age, String email) {
return """
{
"name": "%s",
"age": %d,
"email": "%s"
}
""".formatted(name, age, email);
}
// 正则表达式(更清晰)
String regex = """
\\d{4}-\\d{2}-\\d{2} # 日期格式 YYYY-MM-DD
""";
// 多行日志消息
String logMessage = """
[ERROR] 数据库连接失败
主机: db-server-01
端口: 3306
原因: 连接超时
重试次数: 3
""";
1.3 编译与运行
文本块在 JDK 13 中是预览特性,需要额外编译参数:
# 编译
javac --enable-preview --release 13 App.java
# 运行
java --enable-preview -cp . App
2. Switch 表达式增强(第二次预览)
2.1 概述
JEP 354 — Switch Expressions 在 JDK 12(JEP 325)首次以预览形式引入,JDK 13 中进行了第二次预览。主要变化是引入了 yield 关键字 替代 break 来从 switch 表达式中返回值,使语义更清晰。
JDK 12 vs JDK 13 对比:
- JDK 12:使用
break返回值(与 break 语句语义冲突) - JDK 13:使用
yield返回值(新关键字,语义明确)
2.2 代码案例
// ============ 1. yield 关键字(JDK 13 新增) ============
// JDK 12 写法(已废弃)
// int numLetters = switch (day) {
// case MONDAY, FRIDAY, SUNDAY: break 6;
// case TUESDAY: break 7;
// ...
// };
// JDK 13 写法(使用 yield)
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
int numLetters = switch (Day.MONDAY) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case WEDNESDAY, THURSDAY -> 9;
case SATURDAY -> 8;
};
System.out.println(numLetters); // 6
// ============ 2. yield 用于冒号形式 ============
// 当使用传统冒号形式(:)时,需要用 yield 返回值
String describe = switch ("JAVA") {
case "JAVA":
yield "Java 编程语言"; // 使用 yield 返回
case "PYTHON":
yield "Python 编程语言";
default:
yield "未知语言";
};
System.out.println(describe); // "Java 编程语言"
// ============ 3. yield vs break 对比 ============
// JDK 12(旧):break 既用于跳出 switch 又用于返回值,语义混乱
// case A: break 1; // break 有两个含义
// JDK 13(新):yield 专门用于返回值,break 只用于跳出
// case A: yield 1; // 语义清晰
// ============ 4. yield 与标签 break 配合 ============
// 在冒号形式中,yield 可以在循环或嵌套 switch 中使用
String result = switch ("test") {
case "test":
StringBuilder sb = new StringBuilder();
for (char c : "HELLO".toCharArray()) {
if (c == 'L') break; // 这里的 break 是跳出 for 循环
sb.append(c);
}
yield sb.toString(); // yield 返回 switch 表达式的值
default:
yield "default";
};
System.out.println(result); // "HE"
// ============ 5. 箭头形式不需要 yield ============
// 箭头形式(->)直接返回表达式值,不需要 yield
int result2 = switch ("B") {
case "A" -> 1; // 直接返回
case "B" -> 2; // 直接返回
default -> 0;
};
// 箭头形式 + 代码块,需要用 yield
int result3 = switch ("B") {
case "A" -> {
// 做一些处理
yield 1;
}
case "B" -> {
yield 2;
}
default -> {
yield 0;
}
};
// ============ 6. 实际应用场景 ============
// 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";
case 502 -> "Bad Gateway";
case 503 -> "Service Unavailable";
default -> "Unknown Status";
};
}
// 复杂业务逻辑
String classify(int score) {
return switch (score / 10) {
case 10, 9 -> {
String grade = "A";
yield grade + "(优秀)";
}
case 8 -> {
String grade = "B";
yield grade + "(良好)";
}
case 7 -> {
String grade = "C";
yield grade + "(中等)";
}
case 6 -> {
String grade = "D";
yield grade + "(及格)";
}
default -> {
yield "F(不及格)";
}
};
}
System.out.println(classify(85)); // "B(良好)"
3. 动态 CDS 归档
3.1 概述
JEP 350 — Dynamic Class-Data Sharing(动态类数据共享)。CDS 是 JDK 5 引入的技术,用于在多个 JVM 之间共享类元数据,减少启动时间和内存占用。JDK 13 之前,创建 CDS 归档需要手动执行多个步骤(生成类列表、创建归档)。JDK 13 实现了动态创建 CDS 归档,无需手动操作。
改进点:
- 旧版 CDS(AppCDS,JDK 10):需要三次运行(dump class list → create archive → use archive)
- JDK 13 动态 CDS:在应用退出时自动创建归档(
-XX:ArchiveClassesAtExit),下次启动直接加载(-XX:SharedArchiveFile)
3.2 使用方式
# ============ 1. 创建动态 CDS 归档 ============
# 第一次运行:执行应用并生成归档
java -XX:ArchiveClassesAtExit=app-cds.jsa -jar myapp.jar
# 后续运行:加载归档启动
java -XX:SharedArchiveFile=app-cds.jsa -jar myapp.jar
# ============ 2. 传统 CDS 流程(JDK 10,对比) ============
# 步骤1:生成类列表
java -XX:DumpLoadedClassList=classes.lst -jar myapp.jar
# 步骤2:创建归档
java -Xshare:dump -XX:SharedClassListFile=classes.lst \
-XX:SharedArchiveFile=classes.jsa -jar myapp.jar
# 步骤3:使用归档
java -Xshare:on -XX:SharedArchiveFile=classes.jsa -jar myapp.jar
# ============ 3. 动态 CDS 的优势 ============
# 1. 简化流程:从3步减少到2步
# 2. 自动包含应用类:不需要手动生成类列表
# 3. 归档内容更完整:包含运行时实际加载的所有类
3.3 实际应用场景
# 微服务启动优化
# 第一次:生成归档
java -XX:ArchiveClassesAtExit=service.jsa \
-XX:SharedClassListFile=service-classes.lst \
-jar microservice.jar
# 后续启动(启动时间减少 20%-40%)
java -XX:SharedArchiveFile=service.jsa \
-jar microservice.jar
# Docker 容器中使用
# Dockerfile 示例:
# RUN java -XX:ArchiveClassesAtExit=/app/cds.jsa -jar /app/app.jar || true
# CMD java -XX:SharedArchiveFile=/app/cds.jsa -jar /app/app.jar
4. ZGC 未使用内存回收
4.1 概述
JEP 351 — ZGC(Z Garbage Collector)在 JDK 11(JEP 333)作为实验性特性引入,JDK 13 增强了 ZGC,使其能够将未使用的堆内存归还给操作系统。
核心改进:
- ZGC 之前:堆内存一旦分配给 JVM,即使未使用也不会归还 OS
- ZGC 之后(JDK 13):自动检测未使用内存并归还给操作系统
- 对延迟无影响:内存回收操作不会增加 GC 停顿时间
适用场景:
- 容器化部署(Docker/K8s):内存按需使用,避免资源浪费
- 长时间运行的应用:内存使用量会波动,空闲时归还 OS
- 多应用共享物理机:提高资源利用率
4.2 代码案例
// ============ 1. 启用 ZGC ============
// 命令行参数
// java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar app.jar
// ============ 2. 内存归还演示 ============
public class ZGCMemoryDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println("初始堆内存使用情况:");
printMemoryUsage();
// 分配大量内存
System.out.println("\n分配大量临时数据...");
List<byte[]> buffers = new ArrayList<>();
for (int i = 0; i < 100; i++) {
buffers.add(new byte[10 * 1024 * 1024]); // 10MB × 100 = 1GB
}
System.out.println("分配后堆内存使用情况:");
printMemoryUsage();
// 释放引用
System.out.println("\n释放引用,等待 GC...");
buffers.clear();
buffers = null;
// 触发 GC
System.gc();
Thread.sleep(2000);
System.out.println("GC 后堆内存使用情况:");
printMemoryUsage();
// ZGC 会将未使用的内存归还给 OS
// 可以看到堆使用量显著下降
}
static void printMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
long max = runtime.maxMemory() / 1024 / 1024;
long total = runtime.totalMemory() / 1024 / 1024;
long free = runtime.freeMemory() / 1024 / 1024;
long used = total - free;
System.out.printf(" 最大堆: %d MB%n", max);
System.out.printf(" 已分配: %d MB%n", total);
System.out.printf(" 已使用: %d MB%n", used);
System.out.printf(" 空闲: %d MB%n", free);
}
}
// ============ 3. 容器环境下的配置 ============
// Docker 中配合 -XX:MaxRAMPercentage 使用
// docker run -m 2g java \
// -XX:+UnlockExperimentalVMOptions \
// -XX:+UseZGC \
// -XX:MaxRAMPercentage=75.0 \
// -jar app.jar
//
// 效果:
// - 容器限制 2GB 内存
// - ZGC 最大使用 1.5GB(75%)
// - 空闲时自动归还内存给 OS
// - 其他容器可以使用剩余内存
5. Socket API 重写
5.1 概述
JEP 353 — 重新实现旧版 Socket API。java.net.Socket、ServerSocket、DatagramSocket 等类的底层实现是 1990 年代编写的,代码复杂、难以维护,且存在缓冲区溢出等安全隐患。JDK 13 用全新的实现替代了旧实现。
改进点:
- 新实现更简洁、更易维护
- 使用 Java NIO 的底层机制
- 更好的错误处理
- 为未来网络编程改进奠定基础
- 默认使用新实现,可通过
-Djdk.net.useOldImpl回退
注意:这是底层实现替换,API 不变,现有代码无需修改。
5.2 代码案例
// ============ 1. Socket API 使用(不变) ============
// 客户端
try (Socket socket = new Socket("localhost", 8080)) {
OutputStream out = socket.getOutputStream();
out.write("Hello Server".getBytes());
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = in.read(buffer);
System.out.println("收到响应: " + new String(buffer, 0, len));
}
// ============ 2. ServerSocket(不变) ============
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println("服务器启动,监听端口 8080");
while (true) {
Socket client = serverSocket.accept();
// 处理客户端连接
new Thread(() -> {
try (client) {
InputStream in = client.getInputStream();
byte[] buffer = new byte[1024];
int len = in.read(buffer);
System.out.println("收到: " + new String(buffer, 0, len));
OutputStream out = client.getOutputStream();
out.write("Hello Client".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
// ============ 3. DatagramSocket(不变) ============
// UDP 发送
try (DatagramSocket socket = new DatagramSocket()) {
String message = "Hello UDP";
byte[] data = message.getBytes();
InetAddress address = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(data, data.length, address, 9999);
socket.send(packet);
}
// UDP 接收
try (DatagramSocket socket = new DatagramSocket(9999)) {
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
System.out.println("收到: " + new String(packet.getData(), 0, packet.getLength()));
}
// ============ 4. 回退到旧实现(如需要) ============
// 如果新实现出现问题,可以通过系统属性回退
// java -Djdk.net.useOldImpl=true -jar app.jar
// ============ 5. 新实现的底层改进 ============
// 1. 使用 NIO 的 SocketChannel 作为底层
// 2. 更好的线程安全性
// 3. 更清晰的错误码映射
// 4. 移除了旧实现中的 native 代码依赖
// 性能对比(概念性)
// 旧实现:直接调用 BSD socket API(C 代码)
// 新实现:通过 NIO 的 UnixDomainSocket 等抽象层
6. String.formatted() 方法
6.1 概述
JDK 13 为 String 类新增了 formatted(Object... args) 实例方法,等价于 String.format(this, args),让字符串格式化更加直观和流畅。
6.2 代码案例
// ============ 1. 基础用法 ============
// 传统写法
String s1 = String.format("Hello, %s! You are %d years old.", "Alice", 25);
// JDK 13 新写法(更直观)
String s2 = "Hello, %s! You are %d years old.".formatted("Alice", 25);
// "Hello, Alice! You are 25 years old."
// ============ 2. 与文本块配合(最佳搭档) ============
// 文本块 + formatted() 是绝配
String userJson = """
{
"name": "%s",
"age": %d,
"email": "%s",
"active": %b
}
""".formatted("Alice", 25, "alice@example.com", true);
System.out.println(userJson);
// {
// "name": "Alice",
// "age": 25,
// "email": "alice@example.com",
// "active": true
// }
// ============ 3. SQL 模板 ============
String sql = """
SELECT * FROM users
WHERE name LIKE '%s'
AND age BETWEEN %d AND %d
AND status = '%s'
ORDER BY created_at DESC
LIMIT %d
""".formatted("%Alice%", 18, 35, "ACTIVE", 50);
// ============ 4. 日志格式化 ============
String logEntry = "[%s] [%s] [%s] %s".formatted(
java.time.LocalDateTime.now(),
"INFO",
"UserService",
"User login successful: alice@example.com"
);
// ============ 5. 数字格式化 ============
String price = "商品价格: ¥%,.2f".formatted(1234567.89);
// "商品价格: ¥1,234,567.89"
String percent = "完成率: %.1f%%".formatted(85.5);
// "完成率: 85.5%"
// ============ 6. 与文本块结合的实际场景 ============
// HTML 模板
String htmlTemplate = """
<!DOCTYPE html>
<html>
<head><title>%s</title></head>
<body>
<h1>%s</h1>
<p>欢迎, %s! 您有 %d 条未读消息。</p>
</body>
</html>
""".formatted("消息中心", "消息中心", "Alice", 5);
// 配置文件模板
String configTemplate = """
server:
port: %d
host: %s
database:
url: jdbc:mysql://%s:%d/%s
pool-size: %d
""".formatted(8080, "0.0.0.0", "localhost", 3306, "mydb", 20);
7. FileSystems 新工厂方法
7.1 概述
JDK 13 为 java.nio.file.FileSystems 类新增了工厂方法,简化创建自定义文件系统实例的过程。
7.2 代码案例
// ============ 1. 新增的工厂方法 ============
// JDK 13 新增:
// FileSystems.newFileSystem(Path path, Map<String, ?> env)
// FileSystems.newFileSystem(Path path, ClassLoader classLoader)
// ============ 2. 创建 ZIP/JAR 文件系统 ============
import java.nio.file.*;
import java.net.URI;
import java.util.Map;
// 创建 ZIP 文件系统(旧方式)
Path zipPath = Path.of("example.zip");
URI uri = URI.create("jar:file:" + zipPath.toUri().getPath());
// JDK 13 新方式(更简洁)
Map<String, String> env = Map.of("create", "true");
try (FileSystem zipFs = FileSystems.newFileSystem(zipPath, env)) {
// 在 ZIP 中创建文件
Path entry = zipFs.getPath("README.txt");
Files.writeString(entry, "Hello from ZIP filesystem!");
Path dir = zipFs.getPath("data");
Files.createDirectory(dir);
Path dataFile = dir.resolve("config.json");
Files.writeString(dataFile, """
{
"version": "1.0",
"name": "example"
}
""");
}
// ============ 3. 读取 ZIP 文件内容 ============
Path zipFile = Path.of("archive.zip");
try (FileSystem zipFs = FileSystems.newFileSystem(zipFile, (ClassLoader) null)) {
// 遍历 ZIP 中的所有文件
Files.walk(zipFs.getPath("/"))
.filter(Files::isRegularFile)
.forEach(path -> {
try {
System.out.println(path + " (" + Files.size(path) + " bytes)");
} catch (Exception e) {
e.printStackTrace();
}
});
}
// ============ 4. 实际应用场景 ============
// 批量操作 JAR 文件
Path jarPath = Path.of("myapp.jar");
try (FileSystem jarFs = FileSystems.newFileSystem(jarPath, (ClassLoader) null)) {
// 查找所有 .class 文件
long classCount = Files.walk(jarFs.getPath("/"))
.filter(p -> p.toString().endsWith(".class"))
.count();
System.out.println("JAR 中包含 " + classCount + " 个 class 文件");
}
8. NIO ByteBuffer 增强
8.1 概述
JDK 13 为 ByteBuffer 及相关 Buffer 类新增了多个方法,支持以只读方式访问底层字节数组,以及更灵活地创建 Buffer 视图。
8.2 代码案例
// ============ 1. ByteBuffer 新增方法 ============
// 新增方法:
// ByteBuffer.wrap(byte[] array, int offset, int length) — 已有
// 新增的只读视图方法
// ============ 2. 创建只读 ByteBuffer ============
byte[] data = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello"
// 创建只读包装
ByteBuffer readOnly = ByteBuffer.wrap(data).asReadOnlyBuffer();
// readOnly.put((byte) 0x00); // 抛出 ReadOnlyBufferException
// ============ 3. 切片与视图 ============
ByteBuffer buffer = ByteBuffer.allocate(64);
// 写入数据
buffer.putInt(42);
buffer.putLong(123456789L);
buffer.putDouble(3.14);
// 翻转读取
buffer.flip();
// 读取
int intVal = buffer.getInt(); // 42
long longVal = buffer.getLong(); // 123456789
double doubleVal = buffer.getDouble(); // 3.14
// ============ 4. 实际应用场景:网络协议解析 ============
// 解析自定义二进制协议
byte[] packet = new byte[32];
// ... 从网络接收数据 ...
ByteBuffer buf = ByteBuffer.wrap(packet);
buf.order(ByteOrder.BIG_ENDIAN);
// 解析头部
short version = buf.getShort(); // 2字节版本号
short type = buf.getShort(); // 2字节消息类型
int length = buf.getInt(); // 4字节数据长度
long timestamp = buf.getLong(); // 8字节时间戳
// 解析数据体
byte[] payload = new byte[length];
buf.get(payload);
System.out.printf("版本: %d, 类型: %d, 长度: %d, 时间戳: %d%n",
version, type, length, timestamp);
9. JVM Constants API 更新
9.1 概述
JDK 13 对 JVM Constants API(JDK 12 引入,JEP 334)进行了小幅更新,增加了新的常量类型描述符,更好地支持类文件中的常量池操作。
9.2 代码案例
// ============ 1. ClassDesc — 类描述符 ============
import java.lang.constant.*;
// 创建类描述符
ClassDesc stringDesc = ClassDesc.of("java.lang.String");
ClassDesc intDesc = ClassDesc.of("int");
ClassDesc listDesc = ClassDesc.of("java.util.List");
// 数组类型
ClassDesc stringArrayDesc = stringDesc.arrayType(); // String[]
ClassDesc intArrayDesc = intDesc.arrayType(2); // int[][]
System.out.println(stringArrayDesc.descriptorString()); // "[Ljava/lang/String;"
System.out.println(intArrayDesc.descriptorString()); // "[[I"
// ============ 2. MethodTypeDesc — 方法类型描述符 ============
// 描述方法签名
MethodTypeDesc methodDesc = MethodTypeDesc.of(
ClassDesc.of("void"), // 返回类型
ClassDesc.of("java.lang.String"), // 参数1
ClassDesc.of("int") // 参数2
);
System.out.println(methodDesc.descriptorString());
// "(Ljava/lang/String;I)V"
// ============ 3. MethodHandleDesc — 方法句柄描述符 ============
// 描述方法句柄(包括调用类型)
MethodHandleDesc handleDesc = MethodHandleDesc.ofMethod(
DirectMethodHandleDesc.Kind.VIRTUAL, // 虚方法调用
ClassDesc.of("java.lang.String"), // 所属类
"length", // 方法名
MethodTypeDesc.of(ClassDesc.of("int")) // ()I
);
System.out.println(handleDesc);
// ============ 4. DynamicConstantDesc — 动态常量描述符 ============
// 描述 invokedynamic 引导方法
DynamicConstantDesc<String> dcd = DynamicConstantDesc.ofNamed(
MethodHandleDesc.ofMethod(
DirectMethodHandleDesc.Kind.STATIC,
ClassDesc.of("java.lang.runtime.SwitchBootstraps"),
"typeSwitch",
MethodTypeDesc.of(
ClassDesc.of("int"),
ClassDesc.of("java.lang.invoke.MethodHandles$Lookup"),
ClassDesc.of("java.lang.String"),
ClassDesc.ofDescriptor("[Ljava.lang.invoke.TypeDescriptor;"),
ClassDesc.of("int"),
ClassDesc.ofDescriptor("[Ljava.lang.Object;")
)
),
"switch", // 常量名
ClassDesc.of("int") // 类型
);
// ============ 5. 实际应用场景 ============
// 字节码生成工具中使用
class BytecodeGenerator {
// 生成方法调用的字节码描述
void generatePrintln() {
MethodHandleDesc println = MethodHandleDesc.ofMethod(
DirectMethodHandleDesc.Kind.VIRTUAL,
ClassDesc.of("java.io.PrintStream"),
"println",
MethodTypeDesc.of(
ClassDesc.of("void"),
ClassDesc.of("java.lang.String")
)
);
// 可用于 ASM、ByteBuddy 等字节码操作库
}
}
10. SSL/TLS 增强
10.1 概述
JDK 13 在 SSL/TLS 方面进行了增强,新增了系统属性支持,并改进了 SSLSocket 和 SSLEngine 的行为。
10.2 代码案例
// ============ 1. 新增系统属性 ============
// jdk.tls.keyLimits — 密钥使用量限制
// 格式: <key-type> <max-usage>
// 示例: -Djdk.tls.keyLimits=AES/GCM/NoPadding max:2^37
// 当密钥使用超过限制时,JDK 会自动重新协商
// 防止密码学攻击(如 Sweet32 攻击)
// ============ 2. SSLSocket 增强 ============
import javax.net.ssl.*;
import java.io.*;
// 创建 SSL Socket
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
try (SSLSocket socket = (SSLSocket) factory.createSocket("example.com", 443)) {
// 启用特定协议
socket.setEnabledProtocols(new String[]{"TLSv1.3", "TLSv1.2"});
// 获取会话信息
SSLSession session = socket.getSession();
System.out.println("协议: " + session.getProtocol());
System.out.println("密码套件: " + session.getCipherSuite());
// 发送 HTTPS 请求
OutputStream out = socket.getOutputStream();
out.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".getBytes());
out.flush();
// 读取响应
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// ============ 3. 配置密钥使用限制 ============
// 在代码中设置
System.setProperty("jdk.tls.keyLimits", "AES/GCM/NoPadding max:2^37");
// 或在命令行
// java -Djdk.tls.keyLimits="AES/GCM/NoPadding max:2^37" -jar app.jar
// ============ 4. 实际应用场景 ============
// 高安全要求的服务端配置
SSLContext context = SSLContext.getInstance("TLSv1.3");
context.init(null, null, null);
SSLServerSocketFactory serverFactory = context.getServerSocketFactory();
SSLServerSocket serverSocket = (SSLServerSocket) serverFactory.createServerSocket(8443);
// 仅允许 TLS 1.3
serverSocket.setEnabledProtocols(new String[]{"TLSv1.3"});
// 设置强密码套件
serverSocket.setEnabledCipherSuites(new String[]{
"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256"
});
11. javax.swing 增强
11.1 概述
JDK 13 对 Swing 进行了小幅增强,主要改进了多显示器支持和 JEditorPane 的 HTML 渲染。
11.2 代码案例
// ============ 1. 多显示器改进 ============
import javax.swing.*;
import java.awt.*;
// JDK 13 改进了多显示器环境下的窗口定位
// GraphicsConfiguration 的行为更加一致
JFrame frame = new JFrame("多显示器测试");
frame.setSize(400, 300);
// 获取所有屏幕
GraphicsDevice[] screens = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getScreenDevices();
System.out.println("检测到 " + screens.length + " 个显示器");
for (int i = 0; i < screens.length; i++) {
DisplayMode mode = screens[i].getDisplayMode();
System.out.printf("屏幕 %d: %dx%d @ %dHz%n",
i, mode.getWidth(), mode.getHeight(), mode.getRefreshRate());
}
// 在指定屏幕显示
if (screens.length > 1) {
GraphicsConfiguration gc = screens[1].getDefaultConfiguration();
frame.setBounds(gc.getBounds().x + 50, gc.getBounds().y + 50, 400, 300);
}
frame.setVisible(true);
// ============ 2. JEditorPane HTML 改进 ============
JEditorPane editorPane = new JEditorPane();
editorPane.setContentType("text/html");
editorPane.setText("""
<html>
<body>
<h2>JDK 13 Swing 增强</h2>
<p>HTML 渲染得到了改进。</p>
<ul>
<li>更好的 CSS 支持</li>
<li>改进的表格渲染</li>
<li>更一致的字体处理</li>
</ul>
</body>
</html>
""");
12. JDK 13 特性总览表
| 序号 | 特性 | JEP | 类型 | 重要性 |
|---|---|---|---|---|
| 1 | 文本块 Text Blocks(预览) | JEP 355 | 语言 | ★★★★★ |
| 2 | Switch 表达式增强(第二次预览) | JEP 354 | 语言 | ★★★★☆ |
| 3 | 动态 CDS 归档 | JEP 350 | 性能 | ★★★★☆ |
| 4 | ZGC 未使用内存回收 | JEP 351 | GC | ★★★★☆ |
| 5 | Socket API 重写 | JEP 353 | 平台 | ★★★☆☆ |
| 6 | String.formatted() 方法 | — | API | ★★★★☆ |
| 7 | FileSystems 新工厂方法 | — | API | ★★☆☆☆ |
| 8 | NIO ByteBuffer 增强 | — | API | ★★☆☆☆ |
| 9 | JVM Constants API 更新 | — | API | ★★☆☆☆ |
| 10 | SSL/TLS 密钥使用限制 | — | 安全 | ★★★☆☆ |
| 11 | javax.swing 增强 | — | UI | ★☆☆☆☆ |
附录:JDK 13 编译与运行注意事项
# 预览特性需要额外参数
# 编译
javac --enable-preview --release 13 MyClass.java
# 运行
java --enable-preview -cp . MyClass
# 非预览特性正常使用
java -jar myapp.jar # 动态 CDS、ZGC 等不需要 --enable-preview
预览特性状态追踪:
| 特性 | JDK 12 | JDK 13 | JDK 14 | JDK 15 |
|---|---|---|---|---|
| Switch 表达式 | 预览 | 第二次预览 | 正式 | — |
| Text Blocks | — | 预览 | 第二次预览 | 正式 |
| instanceof 模式匹配 | — | — | 预览 | 第二次预览 |
总结:JDK 13 是一个承前启后的版本。Text Blocks(预览) 为后续正式版本铺路,yield 关键字 解决了 Switch 表达式的语义问题,动态 CDS 和 ZGC 内存回收 提升了启动性能和资源利用率,Socket API 重写 为网络编程打下更坚实的基础。虽然特性数量不多,但每一项都有明确的价值。
更多推荐


所有评论(0)