JDK 13 新特性详解

JDK 13 新特性详解

发布日期 :2019 年 9 月 17 日

规范版本 :Java SE 13(JSR 388)

是否 LTS :否

主题 :文本块首次预览、yield 取代 break value、动态 CDS 归档


一、版本总览

JDK 13 是一个「打磨型」版本,共集成 5 个 JEP。核心价值在于:

  1. 文本块(Text Blocks) 首次预览------解决 Java 多行字符串的历史痛点
  2. Switch 表达式第二次预览 ------用 yield 替换 break value
  3. 动态 CDS 归档------应用退出时自动生成归档,AppCDS 体验大幅改善
  4. 旧版 Socket API 重写------为虚拟线程铺路

二、核心特性详解

1. JEP 355:文本块(Text Blocks,预览)

背景 :Java 长期缺乏多行字符串字面量。写 SQL、JSON、HTML 时充斥着 \n 与 + 拼接,可读性极差。

java 复制代码
// 旧方式:难以阅读和维护
String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, World!</p>\n" +
              "    </body>\n" +
              "</html>\n";

String query = "SELECT id, name, email\n" +
               "FROM users\n" +
               "WHERE age > 18\n" +
               "ORDER BY name";

文本块方式:

java 复制代码
String html = """
        <html>
            <body>
                <p>Hello, World!</p>
            </body>
        </html>
        """;

String query = """
        SELECT id, name, email
        FROM users
        WHERE age > 18
        ORDER BY name
        """;

语法规则:

① 定界符:三个双引号 """ + 换行

  • 开始定界符 """ 后必须换行,否则编译错误
  • 结束定界符可以单独一行,也可以在最后一行内容之后
java 复制代码
String s1 = """
        line1
        line2""";     // 合法:结束定界符紧跟内容

String s2 = """
        line1
        line2
        """;          // 合法:结束定界符独立一行

String bad = """line1
        line2""";     // 编译错误:开始定界符后必须有换行

② 缩进处理(Incidental Whitespace 剥离)

文本块会自动剥离所有行共有的最小缩进,同时保留「有意的额外缩进」。

java 复制代码
String s = """
        a
          b
        c
        """;
// 结果:"a\n  b\nc\n"
// 说明:
//   - 最小缩进 = 8 空格("a"、"c"、"\"\"\"" 行)
//   - "b" 行有 10 空格,剥离 8 后剩 2 → "  b"
//   - "a"、"c" 剥离 8 后剩 0

③ 转义序列

转义 含义
\n 换行(普通换行也可直接写在文本块中)
\t 制表符
\s 新增:空格(用于保留行尾空格)
\ + 换行 新增:行连接,抑制该处换行
\" 双引号
\\ 反斜杠
\uXXXX Unicode 转义
java 复制代码
// \s 保留行尾空格
String colored = """
        red  \s
        green\s
        """;

// \ 行连接:抑制换行,写超长行
String longLine = """
        This is a very long line that \
        continues here without a newline.
        """;
// 结果:"This is a very long line that continues here without a newline.\n"

④ 末尾换行 :若结束定界符独立成行,结果字符串以 \n 结尾。

java 复制代码
"""
    a
    """          // => "a\n"

"""
    a"""         // => "a"

⑤ 文本块中的 """ 与 \"""

java 复制代码
String quotes = """
        He said \"""Hello\"""
        """;

⑥ 文本块是 String 类型

java 复制代码
// 可用于任何需要 String 的地方
String sql = """
        SELECT * FROM t WHERE id = ?
        """;
PreparedStatement ps = conn.prepareStatement(sql);

⑦ 文本块与字符串方法

java 复制代码
String json = """
        {"name":"Alice"}
        """;

json.strip();              // 去除首尾空白
json.stripIndent();        // 智能剥离缩进(JDK 12 引入)
json.translateEscapes();   // 处理转义序列(JDK 15 引入)
json.formatted("x");       // 等价于 String.format(JDK 15 引入)

启用方式(JDK 13 是预览特性):

bash 复制代码
javac --enable-preview --release 13 Example.java
java --enable-preview Example

2. JEP 354:Switch 表达式(第二次预览)

在 JDK 12 预览(JEP 325)的基础上,把 break value 改为 yield value。

为什么改? break 在两种场景下语义混淆:

java 复制代码
// JDK 12:break 既用于跳出 switch,又用于返回值 ------ 容易误解
int x = switch (k) {
    case 1:
        break 10;
    default:
        break 20;
};

// 嵌套在循环中时更混乱:
for (int i = 0; i < 10; i++) {
    int x = switch (i) {
        case 1: break 10;    // 这个 break 是跳出循环还是返回值?
        default: break 20;
    };
}

JDK 13 的 yield:

java 复制代码
int x = switch (k) {
    case 1 -> 10;                        // 箭头形式直接返回值
    case 2 -> { int t = f(); yield t; }  // 块形式用 yield
    default -> 20;
};

yield 的语义 :终止当前 switch 表达式的求值,把值作为 switch 表达式的结果。

关键规则:

  • yield 只能出现在 switch 表达式的块分支中
  • yield 不是 关键字,仍是上下文关键字(int yield = 1; 合法)
  • yield 与 break 的区别:
    • break 跳出循环 或 switch 语句
    • yield 从 switch 表达式返回值
java 复制代码
// 对比示例
int result = switch (day) {
    case MONDAY -> 1;
    case FRIDAY -> {
        System.out.println("Almost weekend");   // 允许副作用
        yield 5;                                 // 必须 yield 值
    }
    default -> {
        yield 0;
    }
};

注意 :JDK 13 预览版同时 支持 yield 和(已弃用的)break value,但后者打印弃用警告。JDK 14 正式版中 break value 被移除。

3. JEP 350:动态 CDS 归档(Dynamic CDS Archives)

背景 :JDK 10 的 AppCDS 需要「先试运行 → 生成列表 → dump 归档 → 正式运行」四步。JDK 13 简化为一步:

bash 复制代码
# JDK 13:运行时指定归档输出路径,退出时自动生成
java -XX:ArchiveClassesAtExit=app.jsa -cp app.jar com.example.Main

# 下次启动直接使用
java -XX:SharedArchiveFile=app.jsa -cp app.jar com.example.Main

工作原理:

  1. JVM 启动时使用默认 CDS 归档(JDK 核心类)
  2. 应用运行期间,JVM 动态记录额外加载/链接的类
  3. JVM 退出时,把这些类与默认归档的内容合并,写出 app.jsa

关键限制:

  • 动态归档依赖于创建时的类路径:任何类路径变化都需要重新生成
  • 归档与 JDK 版本、CPU 架构绑定
  • 不支持在归档中包含自定义类加载器加载的类

与后续版本的关系:

版本 JEP 内容
10 310 AppCDS(手动)
12 341 默认 CDS 归档
13 350 动态 CDS 归档
19 424 CDS 归档中存放堆对象(JDK 19 预览)
24 483 AOT Class Loading & Linking(Project Leyden)
25 514 AOT 命令行人体工程学
26 516 任意 GC 的 AOT 对象缓存

收益:启动时间提升 10%~30%,显著降低延迟敏感的 Serverless 场景冷启动时间。

4. JEP 353:重新实现旧版 Socket API

背景 :java.net.Socket 和 java.net.ServerSocket 的实现自 JDK 1.0 以来几乎没有大变化,代码古老、难以维护,且阻塞式的 Thread 用线程模型不利于未来虚拟线程的实现。

变更:

  • Socket / ServerSocket 的底层实现 (PlainSocketImpl)替换为基于 NIO 的 NioSocketImpl
  • 公共 API 完全不变 ------ 这是纯粹的内部实现替换
  • 保留 java.net.SocketImpl 抽象类(供 JDK 内部及极少数高级用户使用)

新增系统属性用于回退:

bash 复制代码
# 回退到旧实现(仅在遇到兼容性问题时使用)
java -Djdk.net.usePlainSocketImpl=true MyApp

收益:

  • 更容易维护与修复安全漏洞
  • 为虚拟线程(JEP 444)做好准备
  • 与 NIO 共享底层代码

配套变更 :JDK 15 的 JEP 373 用同样的思路重写了 DatagramSocket。

5. JEP 351:ZGC 归还未使用内存

问题 :ZGC 在 JDK 11 中不会把未使用的堆内存归还操作系统,导致容器环境中 RSS 持续高位。

变更:ZGC 现在会把未使用内存归还 OS。

bash 复制代码
# 相关参数
java -XX:+UseZGC -XX:ZUncommitDelay=300 MyApp   # 300 秒后归还(默认值)
java -XX:-ZUncommit MyApp                        # 禁用归还

触发条件:

  • 堆中空闲区域连续未使用超过 ZUncommitDelay
  • 归还后堆最小不低于 -Xms 指定值

收益:容器环境内存占用显著降低。


三、JDK 13 特性速查表

类别 JEP 特性 状态
语言 355 文本块 预览
语言 354 Switch 表达式(第二次预览,引入 yield) 预览
库 353 重新实现旧版 Socket API 正式(内部实现)
GC 351 ZGC 归还未使用内存 正式
运行时 350 动态 CDS 归档 正式

四、升级注意事项

  1. 文本块与 Switch 表达式仍是预览特性 ,需 --enable-preview,无兼容性保证
  2. break value 已弃用 ,改用 yield
  3. 动态 CDS 归档与路径强绑定:迁移应用目录或依赖版本时需重新生成
  4. Socket 实现变更 :极端情况下(依赖旧的实现细节)可能有行为差异;用 -Djdk.net.usePlainSocketImpl=true 排查
  5. ZGC 内存归还:容器内存监控需重新校准

五、参考资源


← JDK 12 | 返回索引 | JDK 14 →

相关推荐
秋饼2 小时前
Java 后端企业级 LLM 网关实战:Spring Cloud Gateway 构建统一大模型接入层
java·ai·技术分享·后端开发
今年下半年2 小时前
【防重放】Spring Boot 通过自定义注解实现接口防重放
java·spring boot·自定义注解·防重放
念越2 小时前
题库、练习与错题本代码阅读
开发语言·数据结构
WeiXin_DZbishe2 小时前
基于Node.js的马拉松赛事管理系统的设计与实现-计算机毕设 附源码【课程设计】90211
java·javascript·vue.js·python·php·课程设计
l1t2 小时前
几种数据库软件count(distinct)和count()from group by子查询性能测试
开发语言·数据库
熊猫钓鱼>_>2 小时前
大学图论第 6 讲:网络流与二分图匹配——对偶性让瓶颈自己现形
开发语言·算法·面试·职场和发展·图论·校招·面经
方便面不加香菜2 小时前
C++ 关于多态的知识点讲解
开发语言·c++
Wang's Blog2 小时前
Java 项目部署之 Docker工具快速入门: 使用 Docker 镜像命令练习
java·docker
企业数字化笔记2 小时前
多租户系统从哪里开始设计?租户身份、数据隔离、权限与套餐的整体方案
java·spring boot·后端·junit