全面解析Java 14新特性:开发者必知的语言改进

按照六个月的发布周期传统,继2019年9月17日发布Java 13之后,Java 14作为另一个非LTS(长期支持)版本,计划于2020年3月17日发布。

Java 14特性

以下是Java 14功能的列表:

  • Switch表达式(标准)- JEP 361
  • instanceof的模式匹配(预览)- JEP 305
  • 有用的NullPointerExceptions – JEP 358
  • 记录类(预览)- JEP 359
  • 文本块(第二次预览)- JEP 368
  • 打包工具(孵化器)- JEP 343
  • G1的NUMA感知内存分配 – JEP 345
  • JFR事件流 – JEP 349
  • 非易失性映射字节缓冲区 – JEP 352
  • macOS上的ZGC – JEP 364
  • Windows上的ZGC – JEP 365
  • 外部内存访问API(孵化器)- JEP 370

在Mac OS上安装Java 14

设置步骤:

  • 要开始使用Java 14,请从此处下载JDK。
  • 将tar文件复制并解压到/Library/Java/JavaVirtualMachines目录,如下所示:
$ cd /Library/Java/JavaVirtualMachines  # 进入Java虚拟机安装目录

$ sudo cp ~/Downloads/openjdk-14_osx-x64_bin.tar.gz /Library/Java/JavaVirtualMachines  # 复制下载的文件

$ sudo tar xzf openjdk-14_osx-x64_bin.tar.gz  # 解压文件

$ sudo rm openjdk-14_osx-x64_bin.tar.gz  # 删除压缩包

完成上述步骤后,使用任何文本编辑器打开bash_profile文件。例如,可以使用vim ~/.bash_profile命令打开。将Java 14的路径设置为JAVA_HOME,保存更改,并执行source ~/.bash_profile命令以使更改生效。

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-14.jdk/Contents/Home  # 设置Java 14的环境变量

最后,您已经准备好使用Java 14编译和运行程序。我们将使用JShell,这是一个交互式的REPL(读取-评估-打印循环)命令行工具,用于快速测试Java 14的新功能。

需要注意的是,Java 14中发布的许多功能都处于预览阶段。这意味着虽然它们现在可以完全使用,但将来可能会进行修改。有些功能可能会成为标准,或者在下一个发布周期中被移除。为了测试预览功能,您需要在运行JShell或Java程序时显式地设置–enable-preview参数,如下所示:

jshell --enable-preview  # 启用预览功能的JShell

javac --release 14 --enable-preview Author.java  # 使用Java 14并启用预览功能编译Author.java文件

在接下来的几个部分中,我们将讨论一些语言和JVM的特性。

推荐阅读:在Linux上安装Java 14

1. Switch表达式

这是文章《Java 14 特性》的第2部分(共6部分)。

在过去的两个版本中,即Java 12和Java 13中,Switch表达式一直作为预览功能,现在它们已经在Java 14中成为了标准特性。

  • Java 12为switch表达式引入了lambda语法,从而允许模式匹配使用多个case标签,并防止导致代码冗余的fall-through现象。它还强制执行穷尽性检查,如果未覆盖所有输入情况,将抛出编译错误。
  • Java 13作为第二次预览,引入了yield语句来替代break,用于从表达式返回值。

Java 14终于将这些功能纳入了标准。

String result = switch (day) {
            case "M", "W", "F" -> "周一、三、五";
            case "T", "TH", "S" -> "周二、四、六";
            default -> {
                if(day.isEmpty())
                    yield "请插入有效的日期。";
                else
                    yield "看起来像是周日。";
            }

        };
System.out.println(result);
Java 14 Switch表达式

注意:yield 不是 Java 中的新关键字,它只是在 switch 表达式中使用。

2. instanceof实例的模式匹配(预览)

这是文章《Java 14 特性》的第3部分(共6部分)。

查看任何Java开发人员的代码库,你会看到代码中大量使用了instanceof条件判断。具体来说,一个instanceof条件检查通常会跟随一个类型转换。

Java 14通过使条件提取更加简洁,消除了这种冗余代码。

在Java 14之前:

if (obj instanceof Journaldev) {
  Journaldev jd = (Journaldev) obj;
  System.out.println(jd.getAuthor());
}

从Java 14开始:

if (obj instanceof Journaldev jd) {
  System.out.println(jd.getAuthor());
}

在上述代码中,只有当obj属于Journaldev类型时,实例jd才会被赋值。变量的作用范围仅限于条件块内。

3. 有用的空指针异常

空指针异常对于任何开发者来说都是一个棘手的问题。在Java 13之前,调试空指针异常是一项棘手的任务。开发者必须依赖其他调试工具或手动找出变量/方法是否为空,因为堆栈跟踪只会显示行号。

在Java 14之前:

String name = jd.getBlog().getAuthor()

//堆栈跟踪信息
Exception in thread "main" java.lang.NullPointerException
    at NullPointerExample.main(NullPointerExample.java:5)

Java 14新引入了一项JVM功能,通过更详细的堆栈展示提供更详细的诊断信息。

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Blog.getAuthor()" because the return value of "Journaldev.getBlog()" is null
    at NullPointerExample.main(NullPointerExample.java:4)

注意:上述功能并不是语言特性,而是JVM运行时环境的改进。

4. 记录(预览)

这是文章《Java 14 特性》的第4部分(共6部分)。

内容片段: 记录(Record)是一种专门用于存储纯数据的数据类。引入记录的主要目的是为了能够快速创建简单而简洁、无需编写样板代码的类。

通常情况下,在Java中创建一个类需要实现equals()、hashCode()方法以及getter和setter方法。虽然一些集成开发环境(IDE)支持自动生成这些方法,但代码仍然显得冗长。而有了记录(Record),你只需要按以下方式定义一个类。

record Author(){}
//或者
record Author (String name, String topic) {}

Java 编译器将自动生成构造函数、私有的 final 字段、访问器方法、equals/hashCode 方法以及 toString 方法。对于上述示例类,自动生成的 getter 方法是 name() 和 topic()。

在使用javac对程序进行编译后,您可以使用javap Author命令来查看生成的代码。下面的示例显示了为记录 Author(String name, String topic) {} 生成的类结构。

Javap Records Java 14

记录的语义与Kotlin中的数据类相似。

此外,我们可以按照以下方式向记录中添加额外的字段、方法和构造函数。

record Author (int id, String name, String topic) {
    static int followers;

    public static String followerCount() {
        return "粉丝数量为: "+ followers;
    }

    public String description(){
        return "作者 "+ name + " 擅长写作的主题是 "+ topic;
    }

    public Author{
    if (id < 0) {
        throw new IllegalArgumentException( "id必须大于0");
     }
   }
}

在记录中定义的这种附加构造函数被称为紧凑构造函数(Compact Constructor)。它不包含任何参数,只是规范构造函数的扩展。

编译器不会单独生成紧凑构造函数,而是将其用于数据验证,并在主构造函数开始时调用。

关于记录(Records)需要注意的几个重要事项:

  • 记录(Record)不能继承其他类,也不能被其他类继承。它是一个final类。
  • 记录不能是抽象的(abstract)
  • 记录不能继承任何其他类,并且不能在主体内部定义实例字段。实例字段只能在状态描述中定义
  • 声明的字段是私有的(private)和最终的(final)
  • 记录的主体允许定义静态字段和方法

推荐阅读: Java Records

4.1) 记录中引用字段的值可以被改变

需要注意的是,对于定义为对象的字段,只有引用是不可变的。底层的值仍然可以被修改。下面的示例展示了一个记录,其中ArrayList被修改的情况。正如你所看到的,只要ArrayList的内容被改变,其值就会被修改。

Java 14 Records Mutable Values For References

4.2)记录可以实现接口

这是文章《Java 14 特性》的第5部分(共6部分)。

以下代码展示了使用记录实现接口的示例。

record Author(String name, String topic) implements Information {
  public String getFullName() {
    return "Author "+ name + " writes on " + topic;
  }
}

interface Information {
  String getFullName();
}

以下是在JShell中执行上述代码的输出结果:

Java 14 记录与接口

4.3) 记录支持多个构造函数

如下所示,记录允许声明带参数或不带参数的多个构造函数。

record Author(String name, String topic) {
  public Author() {

    this("NA", "NA");
  }

  public Author(String name) {

    this(name, "NA");
  }
}

4.4) 记录允许修改访问者方法

虽然记录会为状态描述中定义的字段生成公共访问器方法,但它们也允许您在正文中重新定义访问器方法,如下所示。

record Author(String name, String topic) {
  public String name() {
        return "This article was written by " + this.name;
    }
}

4.5)运行时检查记录及其组件

记录为我们提供了isRecord()和getRecordComponents()来检查类是否是一个记录,以及查看它的字段和类型。以下示例展示了如何进行操作。

Java 14 记录运行时检查

尽管在上述代码示例中我们确实添加了额外的字段和方法到记录中,但请确保不要过度使用。记录设计为纯数据载体,如果您希望实现大量额外的方法,最好还是使用普通类。

5. 文本块(预览版)

Java 13引入了文本块作为预览功能,旨在简化多行字符串字面量的创建。它在处理HTML、JSON或SQL查询字符串等场景中特别有用。

在Java 14中,文本块仍处于预览状态,并引入了一些新功能。现在我们可以使用以下特性:

  • 反斜杠用于显示美观的多行字符串块。
  • \s用于保留尾随空格,这些空格默认情况下会被编译器忽略。它会保留其前面的所有空格。
String text = """"
                Did you know \\
                Java 14 \\
                has the most features among\\
                all non-LTS versions so far\\
                """"";

String text2 = """
                line1
                line2 \\s
                line3
                """;


String text3 = "line1\nline2 \nline3\n"

//text2和text3是相等的。

参考资料:OpenJDK 14

bannerAds