Contents

Java 12中的新功能

1. 简介

在本教程中,我们将对 Java 12 附带的一些新功能进行快速、高层次的概述。官方文档 中提供了所有新功能的完整列表。

2. 语言变化和特点

Java 12 引入了许多新的语言特性。在本节中,我们将通过代码示例讨论一些最有趣的内容,以便更好地理解。

2.1. 字符串类新方法

Java 12 在String类 中提供了两个新方法。 第一个 -indent根据整数参数调整每行的缩进。如果参数大于零,将在每行的开头插入新的空格。另一方面,如果参数小于零,它会从每行的开头删除空格。如果给定行不包含足够的空白,则删除所有前导空白字符。 现在,让我们看一个基本的例子。首先,我们将文本缩进四个空格,然后删除整个缩进:

String text = "Hello Blogdemo!\nThis is Java 12 article.";
text = text.indent(4);
System.out.println(text);
text = text.indent(-10);
System.out.println(text);

输出如下所示:

    Hello Blogdemo!
    This is Java 12 article.
Hello Blogdemo!
This is Java 12 article.

请注意,即使我们传递了超过缩进数的值 -10,也只有空格受到影响。其他字符保持不变。 第二个新方法是transform。它接受一个参数函数作为将应用于字符串的参数。 例如,让我们使用 transform 方法还原字符串:

@Test
public void givenString_thenRevertValue() {
    String text = "Blogdemo";
    String transformed = text.transform(value ->
      new StringBuilder(value).reverse().toString()
    );
    assertEquals("gnudleaB", transformed);
}

2.2. file::mismatch方法

Java 12在nio.file.Files 工具类中引入了一个新的mismatch方法:

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

该方法用于比较两个文件并找到第一个不匹配字节在其内容中的位置。 返回值将在 0L 到较小文件的字节大小的包含范围内,或者如果文件相同则为 -1L。 现在让我们来看两个例子。在第一个中,我们将创建两个相同的文件并尝试找出不匹配的地方。返回值应该是-1L:

@Test
public void givenIdenticalFiles_thenShouldNotFindMismatch() {
    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 12 Article”和“Java 12 Tutorial”内容的文件。mismatch 方法应该返回 8L,因为它是第一个不同的字节:

@Test
public void givenDifferentFiles_thenShouldFindMismatch() {
    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);
}

2.3. Teeing

Java 12 中引入了一个新的Teeing收集器作为Collectors 类的补充:

Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1,
  Collector<? super T, ?, R2> downstream2, BiFunction<? super R1, ? super R2, R> merger)

它是两个下游收集器的组合。每个元素都由两个下游收集器处理。然后将它们的结果传递给合并函数并转化为最终结果。 Teeing 收集器的示例用法是计算一组数字的平均值。第一个收集器参数将对值求和,第二个参数将给我们所有数字的计数。合并函数将采用这些结果并计算平均值:

@Test
public void givenSetOfNumbers_thenCalculateAverage() {
    double mean = Stream.of(1, 2, 3, 4, 5)
      .collect(Collectors.teeing(Collectors.summingDouble(i -> i), 
        Collectors.counting(), (sum, count) -> sum / count));
    assertEquals(3.0, mean);
}

2.4. 紧凑的数字格式

Java 12 带有一个新的数字格式化程序 —— CompactNumberFormat。它旨在根据给定语言环境提供的模式以较短的形式表示数字。 我们可以通过NumberFormat类中的getCompactNumberInstance方法获取它的实例:

public static NumberFormat getCompactNumberInstance(Locale locale, NumberFormat.Style formatStyle)

如前所述,locale 参数负责提供正确的格式模式。格式样式可以是 SHORT 或 LONG。为了更好地理解格式样式,让我们考虑美国语言环境中的数字 1000。SHORT 样式会将其格式化为“10K”,而 LONG 样式会将其格式化为“10000”。 现在让我们看一个示例,该示例将采用本文下的点赞数并使用两种不同的样式对其进行压缩:

@Test
public void givenNumber_thenCompactValues() {
    NumberFormat likesShort = 
      NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.SHORT);
    likesShort.setMaximumFractionDigits(2);
    assertEquals("2.59K", likesShort.format(2592));
    NumberFormat likesLong = 
      NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.LONG);
    likesLong.setMaximumFractionDigits(2);
    assertEquals("2.59 thousand", likesLong.format(2592));
}

3. 预览变化

一些新功能仅作为预览提供。要启用它们,我们需要在 IDE 中切换适当的设置或明确告诉编译器使用预览功能:

javac -Xlint:preview --enable-preview -source 12 src/main/java/File.java

3.1. 切换表达式(预览)

Java 12 中引入的最受欢迎的功能是Switch Expressions

作为演示,让我们比较一下新旧 switch 语句。我们将使用它们根据来自LocalDate实例的DayOfWeek枚举来区分工作日和周末。 首先,让我们看一下旧语法:

DayOfWeek dayOfWeek = LocalDate.now().getDayOfWeek();
String typeOfDay = "";
switch (dayOfWeek) {
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        typeOfDay = "Working Day";
        break;
    case SATURDAY:
    case SUNDAY:
        typeOfDay = "Day Off";
}

现在,让我们看看相同的逻辑女巫开关表达式:

typeOfDay = switch (dayOfWeek) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Working Day";
    case SATURDAY, SUNDAY -> "Day Off";
};

新的 switch 语句不仅更加紧凑和可读。它们还消除了对 break 语句的需要。第一次匹配后代码执行不会失败。 另一个显着的区别是我们可以将 switch 语句直接分配给变量。以前是不可能的。 也可以在 switch 表达式中执行代码而不返回任何值:

switch (dayOfWeek) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> System.out.println("Working Day");
    case SATURDAY, SUNDAY -> System.out.println("Day Off");
}

更复杂的逻辑应该用大括号括起来:

case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
    // more logic
    System.out.println("Working Day")
}

请注意,我们可以在旧语法和新语法之间进行选择。Java 12 switch 表达式只是一个扩展,而不是替代。

3.2. instanceof的模式匹配(预览版)

Java 12 中引入的另一个预览功能是instanceof 的模式匹配。 在以前的 Java 版本中,例如,当将 if 语句与instanceof 一起使用时,我们必须显式地对对象进行类型转换才能访问其功能:

Object obj = "Hello World!";
if (obj instanceof String) {
    String s = (String) obj;
    int length = s.length();
}

使用 Java 12,我们可以直接在语句中声明新的类型转换变量:

if (obj instanceof String s) {
    int length = s.length();
}

编译器会自动为我们注入类型转换后的String变量。

4. JVM 的变化

Java 12 附带了几个 JVM 增强功能。在本节中,我们将快速浏览一些最重要的内容。

4.1. Shenandoah:低停顿时间垃圾收集器

Shenandoah 是一种实验性垃圾收集 (GC)  算法,目前未包含在默认的 Java 12 构建中。 它通过与正在运行的 Java 线程同时执行疏散工作来减少 GC 暂停时间。这意味着对于 Shenandoah,暂停时间不依赖于堆的大小并且应该是一致的。收集 200 GB 堆或 2 GB 堆的垃圾应该具有类似的低暂停行为。 自版本 15 以来,Shenandoah 将成为主线 JDK 构建的一部分。

4.2. 微基准套件

Java 12 为 JDK 源代码引入了一套大约 100 个微基准测试。 这些测试将允许在 JVM 上进行持续的性能测试,并且对于希望在 JVM 本身上工作或创建新的微基准测试的每个开发人员都非常有用。

4.3. 默认 CDS 档案

类数据共享 (CDS) 功能有助于减少多个 Java 虚拟机之间的启动时间和内存占用。它使用内置时生成的默认类列表,其中包含选定的核心库类。 Java 12 带来的变化是默认启用 CDS 存档。要在关闭 CDS 的情况下运行程序,我们需要将 Xshare 标志设置为关闭:

java -Xshare:off HelloWorld.java

请注意,这可能会延迟程序的启动时间。