---
title: Java 12  & 13 新特性概览
description: 归纳 JDK 12/13 的特性更新，包含字符串增强、switch 改进与 GC 调整等。
category: Java
tag:
  - Java新特性
head:
  - - meta
    - name: keywords
      content: Java 12,Java 13,字符串增强,切换表达式,垃圾回收,JEP
---

## Java 12

JDK 12 于 2019 年 3 月 19 日发布，这是一个非 LTS 版本。

这篇文章会挑选其中较为重要的一些新特性进行详细介绍：

- [JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental)](https://openjdk.org/jeps/189)
- [JEP 325: Switch Expressions (Preview) (switch 表达式, 预览特性)](https://openjdk.org/jeps/325)
- [JEP 334: JVM Constants API (JVM 常量 API)](https://openjdk.org/jeps/334)
- [JEP 344: Abortable Mixed Collections for G1 (G1 可中止的混合收集集合)](https://openjdk.org/jeps/344)
- [JEP 346: Promptly Return Unused Committed Memory (G1 及时返回未使用的已分配内存)](https://openjdk.org/jeps/346)

下图是从 JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间：

![ JDK 8 到 JDK 25 每个版本的更新带来的新特性数量和更新时间](https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png)

### JEP 189: Shenandoah（低延迟垃圾收集器，实验性）

Redhat 主导开发的 Pauseless GC 实现，主要目标是 99.9% 的暂停小于 10ms，暂停与堆大小无关等

和 Java11 开源的 ZGC 相比（需要升级到 JDK11 才能使用），Shenandoah GC 有稳定的 JDK8u 版本，在 Java8 占据主要市场份额的今天有更大的可落地性。

### JEP 344 & JEP 346: G1 收集器优化

Java12 为默认的垃圾收集器 G1 带来了两项更新:

- **可中止的混合收集集合**：JEP344 的实现，为了达到用户提供的停顿时间目标，JEP 344 通过把要被回收的区域集（混合收集集合）拆分为强制和可选部分，使 G1 垃圾回收器能中止垃圾回收过程。 G1 可以中止可选部分的回收以达到停顿时间目标
- **及时返回未使用的已分配内存**：JEP346 的实现，增强 G1 GC，以便在空闲时自动将 Java 堆内存返回给操作系统

### JEP 334: JVM Constants API（JVM 常量 API）

引入了一个 API 来对关键类文件和运行时工件的名义描述进行建模，特别是可以从常量池加载的常量。

这个 API 提供了一组接口和工具类，用于表示和操作类文件中的常量池条目。它主要包括：

- **常量描述符接口**：`ConstantDesc` 接口及其子接口，用于描述各种类型的常量
- **常量值类型**：`ClassDesc`、`MethodTypeDesc`、`MethodHandleDesc`、`DynamicConstantDesc` 等
- **引导方法**：支持 `invokedynamic` 指令和常量动态引导方法

这个 API 主要是为了支持以下场景：

1. **类文件操作**：提供了一种标准化的方式来描述和操作类文件中的常量池
2. **字节码生成**：简化了字节码生成框架（如 ASM）与 Java 代码的交互
3. **反射增强**：使得反射操作更加类型安全和表达力更强
4. **编译器工具**：为编译器和代码生成工具提供了更好的抽象

这个 API 是 Java 12 中重要的底层改进，为后续的字节码操作和编译器特性奠定了基础。

### JEP 325: Switch Expressions（switch 表达式，预览）

传统的 `switch` 语法存在容易漏写 `break` 的问题，而且从代码整洁性层面来看，多个 break 本质也是一种重复。

Java12 增强了 `switch` 表达式，使用类似 lambda 语法条件匹配成功后的执行块，不需要多写 break。

```java
switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}
```

## Java 13

JDK 13 于 2019 年 9 月 17 日发布，这是一个非 LTS 版本。

这篇文章会挑选其中较为重要的一些新特性进行详细介绍：

- [JEP 350: Dynamic CDS Archives (动态 CDS 存档)](https://openjdk.org/jeps/350)
- [JEP 351: ZGC: Uncommit Unused Memory (ZGC 释放未使用内存)](https://openjdk.org/jeps/351)
- [JEP 355: Text Blocks (Preview) (文本块, 预览特性)](https://openjdk.org/jeps/355)
- [JEP 354: Switch Expressions (Second Preview) (switch 表达式, 第二次预览)](https://openjdk.org/jeps/354)

下图是从 JDK 8 到 JDK 24 每个版本的更新带来的新特性数量和更新时间：

![](https://oss.javaguide.cn/github/javaguide/java/new-features/jdk8~jdk24.png)

### JEP 351: ZGC（释放未使用内存）

在 Java 11 中实验性引入的 ZGC 在实际的使用中存在未能主动将未使用的内存释放给操作系统的问题。

ZGC 堆由一组称为 ZPages 的堆区域组成。在 GC 周期中清空 ZPages 区域时，它们将被释放并返回到页面缓存 **ZPageCache** 中，此缓存中的 ZPages 按最近最少使用（LRU）的顺序，并按照大小进行组织。

在 Java 13 中，ZGC 将向操作系统返回被标识为长时间未使用的页面，这样它们将可以被其他进程重用。

### JEP 350: Dynamic CDS Archives（动态 CDS 存档）

Java 13 中对 Java 10 中引入的应用程序类数据共享(AppCDS)进行了进一步的简化、改进和扩展，即：**允许在 Java 应用程序执行结束时动态进行类归档**，具体能够被归档的类包括所有已被加载，但不属于默认基层 CDS 的应用程序类和引用类库中的类。

这提高了应用程序类数据共享（[AppCDS](https://openjdk.java.net/jeps/310)）的可用性。无需用户进行试运行来为每个应用程序创建类列表。

```bash
java -XX:ArchiveClassesAtExit=my_app_cds.jsa -cp my_app.jar
java -XX:SharedArchiveFile=my_app_cds.jsa -cp my_app.jar
```

### JEP 355: Text Blocks（文本块，预览）

解决 Java 定义多行字符串时只能通过换行转义或者换行连接符来变通支持的问题，引入**三重双引号**来定义多行文本。

Java 13 支持两个 `"""` 符号中间的任何内容都会被解释为字符串的一部分，包括换行符。注意：这里的“两个”应理解为“一对”，即开始和结束各一个。

未支持文本块之前的 HTML 写法：

```java
String json ="{\n" +
              "   \"name\":\"mkyong\",\n" +
              "   \"age\":38\n" +
              "}\n";
```

支持文本块之后的 HTML 写法：

```java
 String json = """
                {
                    "name":"mkyong",
                    "age":38
                }
                """;
```

未支持文本块之前的 SQL 写法：

```sql
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
               "WHERE `CITY` = 'INDIANAPOLIS'\n" +
               "ORDER BY `EMP_ID`, `LAST_NAME`;\n";
```

支持文本块之后的 SQL 写法：

```sql
String query = """
               SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
               WHERE `CITY` = 'INDIANAPOLIS'
               ORDER BY `EMP_ID`, `LAST_NAME`;
               """;
```

文本块相关的方法（`formatted()`、`stripIndent()`、`translateEscapes()`）介绍请参见本文 [API 增强 - String 增强（文本块相关方法）](#string-增强文本块相关方法) 部分。

### JEP 354: Switch Expressions（switch 表达式，第二次预览）

`Switch` 表达式中就多了一个关键字用于跳出 `Switch` 块的关键字 `yield`，主要用于返回一个值

`yield` 和 `return` 的区别在于：`return` 会直接跳出当前循环或者方法，而 `yield` 只会跳出当前 `Switch` 块，同时在使用 `yield` 时，需要有 `default` 条件

```java
 private static String descLanguage(String name) {
        return switch (name) {
            case "Java": yield "object-oriented, platform independent and secured";
            case "Ruby": yield "a programmer's best friend";
            default: yield name +" is a good language";
        };
 }
```

## API 增强

并不是所有的 API 改动都会通过 JEP（Java Enhancement Proposal）来发布。

在 JDK 的开发流程中：**JEP** 通常用于重大的改变，例如引入新的语言特性（如 `switch` 表达式）、新的 JVM 机制（如 ZGC）或者大规模的库重构。像 `String.indent()` 这种在现有类中增加几个方法的操作，通常被视为常规的库维护。它们由 JDK 开发者直接通过 **JBS (JDK Bug System)** 的工单（Ticket）进行提交和评审，然后随版本直接发布。

### String 增强

Java 12 增加了两个的字符串处理方法。

#### indent() - 缩进方法

`indent()` 方法可以实现字符串缩进。

```java
String text = "Java";
// 缩进 4 格
text = text.indent(4);
System.out.println(text);
text = text.indent(-10);
System.out.println(text);
```

输出：

```plain
     Java
Java
```

#### transform() - 转换方法

`transform()` 方法可以用来转变指定字符串。

```java
String result = "foo".transform(input -> input + " bar");
System.out.println(result); // foo bar
```

### Files 增强

Java 12 添加了 `mismatch()` 方法来比较两个文件：

```java
public static long mismatch(Path path, Path path2) throws IOException
```

`mismatch()` 方法用于比较两个文件，并返回第一个不匹配字符的位置，如果文件相同则返回 -1L。

代码示例（两个文件内容相同的情况）：

```java
Path filePath1 = Files.createTempFile("file1", ".txt");
Path filePath2 = Files.createTempFile("file2", ".txt");
Files.writeString(filePath1, "Java 12 Article");
Files.writeString(filePath2, "Java 12 Article");

long mismatch = Files.mismatch(filePath1, filePath2);
assertEquals(-1, mismatch);
```

代码示例（两个文件内容不相同的情况）：

```java
Path filePath3 = Files.createTempFile("file3", ".txt");
Path filePath4 = Files.createTempFile("file4", ".txt");
Files.writeString(filePath3, "Java 12 Article");
Files.writeString(filePath4, "Java 12 Tutorial");

long mismatch = Files.mismatch(filePath3, filePath4);
assertEquals(8, mismatch);
```

### NumberFormat 增强

Java 12 中 `NumberFormat` 新增了对复杂的数字进行格式化的支持：

```java
NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
String result = fmt.format(1000);
System.out.println(result);
```

输出:

```plain
1K
```

### Socket API 增强

Java 13 将 Socket API 的底层进行了重写， `NioSocketImpl` 是对 `PlainSocketImpl` 的直接替代，它使用 `java.util.concurrent` 包下的锁而不是同步方法。如果要使用旧实现，请使用 `-Djdk.net.usePlainSocketImpl=true`。

并且，在 Java 13 中是默认使用新的 Socket 实现。

```java
public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl {
}
```

### FileSystems 增强

Java 13 中 `FileSystems` 类中添加了以下三种新方法，以便更容易地使用将文件内容视为文件系统的文件系统提供程序：

- `newFileSystem(Path)`
- `newFileSystem(Path, Map<String, ?>)`
- `newFileSystem(Path, Map<String, ?>, ClassLoader)`

### String 增强（文本块相关方法）

Java 13 引入了文本块（Text Blocks）预览特性，`String` 类新增加了 3 个新的方法来操作文本块：

- `formatted(Object... args)`：它类似于 `String` 的 `format()` 方法。添加它是为了支持文本块的格式设置。
- `stripIndent()`：用于去除文本块中每一行开头和结尾的空格。
- `translateEscapes()`：转义序列如 _"\\\t"_ 转换为 _"\t"_

由于文本块是一项预览功能，可以在未来版本中删除，因此这些新方法被标记为弃用。

```java
@Deprecated(forRemoval=true, since="13")
public String stripIndent() {
}
@Deprecated(forRemoval=true, since="13")
public String formatted(Object... args) {

}
@Deprecated(forRemoval=true, since="13")
public String translateEscapes() {
}
```

关于文本块的详细介绍，请参见本文 [JEP 355: Text Blocks (Preview)](#jep-355-text-blocks-preview) 部分。

## 补充

### 关于预览特性

先贴一段 oracle 官网原文：`This is a preview feature, which is a feature whose design, specification, and implementation are complete, but is not permanent, which means that the feature may exist in a different form or not at all in future JDK releases. To compile and run code that contains preview features, you must specify additional command-line options.`

这是一个预览功能，该功能的设计，规格和实现是完整的，但不是永久性的，这意味着该功能可能以其他形式存在或在将来的 JDK 版本中根本不存在。 要编译和运行包含预览功能的代码，必须指定其他命令行选项。

就以 `switch` 的增强为例子，从 Java 12 中推出，到 Java 13 中将继续增强，直到 Java 14 才正式转正进入 JDK 可以放心使用，不用考虑后续 JDK 版本对其的改动或修改。

一方面可以看出 JDK 作为标准平台在增加新特性的严谨态度，另一方面个人认为是对于预览特性应该采取审慎使用的态度。特性的设计和实现容易，但是其实际价值依然需要在使用中去验证

### JVM 虚拟机优化

每次 Java 版本的发布都伴随着对 JVM 虚拟机的优化，包括对现有垃圾回收算法的改进，引入新的垃圾回收算法，移除老旧的不再适用于今天的垃圾回收算法等

整体优化的方向是**高效，低时延的垃圾回收表现**

对于日常的应用开发者可能比较关注新的语法特性，但是从一个公司角度来说，在考虑是否升级 Java 平台时更加考虑的是**JVM 运行时的提升**

## 参考

- JDK Project Overview：<https://openjdk.java.net/projects/jdk/>
- Oracle Java12 ReleaseNote：<https://www.oracle.com/java/technologies/javase/12all-relnotes.htm>
- What is new in Java 12：<https://mkyong.com/java/what-is-new-in-java-12/>
- Oracle Java13 ReleaseNote <https://www.oracle.com/technetwork/java/javase/13all-relnotes-5461743.html#NewFeature>
- New Java13 Features <https://www.baeldung.com/java-13-new-features>
- Java13 新特性概述 <https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-13/index.html>

<!-- @include: @article-footer.snippet.md -->
