天天看点

java14新特性Java 14 有哪些新特性?

Java14发布,16大新特性,代码更加简洁明快

  • 现在官方 最新的为 8,11,15
  • 2020/3/17日JDK14正式发版,生产环境用不用再说,赶紧下载下来体验一番。
  • 不过据说该版本并不是长期版本,但即使再发布新版本也是在此基础上的。

另外,想体验Java14还是需要一些准备工作的,除了安装jdk,还需要安装支持java14的idea,比如2020.1版本。当然,如果整个团队都使用该版本,则需要同时升级IDE,否则低版本的IDE无法正确识别。

java14新特性

在JDK14中新增了以下16个新特性:

  • 305: instanceof 的模式匹配 (预览)
  • 343: 打包工具 (Incubator)
  • 345: G1的 NUMA内存分配优化
  • 349: JFR 事件流
  • 352: 非原子性的 字节缓冲区 映射
  • 358: 友好的 空指针异常
  • 359: Records (预览)
  • 361: Switch表达式 (标准)
  • 362: 弃用Solaris 和 SPARC端口
  • 363: 移除CMS(Concurrent Mark Sweep)垃圾收集器
  • 364: macOS系统上的 ZGC
  • 365: Windows系统上的 ZGC
  • 366: 弃用 ParallelScavenge + SerialOld GC组合
  • 367: 移除 Pack200 Tools和 API
  • 368: 文本块 (第二个预览版)
  • 370: 外部存储器 API (Incubator)

通过上述新特性,我们可以看出其中363、364、365、366都是关于垃圾回收器的。其中363已经明确弃用 CMS垃圾收集器。

而其中针对开发过程中可见的特性包括:instanceof模式匹配、Switch表达式、文本块等。下面我们就来体验一下编程过程中能用到 语法形式。

instanceof模式匹配

通常情况下我们使用 instanceof 来探测类的真实类型,如果符合该类型,则可进行强制转换。

在Java14之前,我们通常的写法如下:

Object obj = "程序新视界";
if(obj instanceof String){
	String str = (String) obj;
	System.out.println("关注公众号:" + str);
}
           

通过java14的新特性,我们可以简化成如下方式:

Object obj = "程序新视界";
if(obj instanceof String str){
	System.out.println("关注公众号:" + str);
}
           

我们可以通过模式匹配简洁地表达对象,并允许各种语句和表达式对其进行测试。

Switch表达式

通常情况下我们通过如下形式使用switch语法:

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}
           

java14引入了新形式的switch标签“case L->”,表示如果匹配,则只执行标签右边的代码。switch标签允许在每种情况下使用逗号分隔多个常量。

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);
}
           

是不是简单清晰了很多。

记录类型(Record Type)的引入

Java 14中记录类型 (Record Type)作为预览特性被引入。

记录对象允许使用紧凑的语法来声明类,和枚举类型一样,记录也是类的一种受限形式。

在idea 2020.1中,创建Record与创建类和枚举一样,可以在创建时直接选择对应的类型。

定义一个Record类型如下:

public record Point(int x, int y) {

}
           

使用Record操作如下:

Point point = new Point(1,3);
System.out.println(point.x());
System.out.println(point.y());
           

对Record类进行反编译我们会看到如下内容:

public final class Point extends java.lang.Record {
    private final int x;
    private final int y;

    public Point(int x, int y) { /* compiled code */ }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public int x() { /* compiled code */ }

    public int y() { /* compiled code */ }
}
           

是不是有替代Lombok的潜质?

文本块作为预览特性保留

通常情况下我们定义一个字符串,如果比较长可使用如下形式进行编写:

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";
           

使用java14的文本块新特性,则可改写为如下形式:

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;


        String s3="""
                张三
                李四 
                王五
                """;
    //s3的 长度 为 : 6+3=9 ,每个文字后 都有一个 \n
           

文本块是Java语言的新语法,可以用来表示任何字符串,具有更高的表达能力和

更少的复杂度。

文本块的开头定界符是由三个双引号字符(""")组成的序列,后面跟0个或多个空格,最后跟一个行终止符。内容从开头定界符的行终止符之后的第一个字符开始。

结束定界符是三个双引号字符的序列。内容在结束定界符的第一个双引号之前的最后一个字符处结束。

与字符串文字中的字符不同,文本块的内容中可以直接包含双引号字符。允许在文本块中使用",但不是必需的或不建议使用。

与字符串文字中的字符不同,内容可以直接包含行终止符。允许在文本块中使用\n,但不是必需或不建议使用。

打包工具 (Incubator)

jpackage打包工具可以将Java应用程序打包为针对特定平台的安装包,这个安装包包含所有必需的依赖项。该应用程序可以以普通JAR文件集合或模块集合的方式提供。软件包格式可以分为:

  • Linux:deb和rpm
  • macOS:pkg和dmg
  • Windows:msi和exe

小结

当然,java14还有许多其他特性的优化,比如垃圾回收器的ParallelScavenge + SerialOld的弃用等。但在使用之前,还是需要慎重,毕竟很多特性都只是作为预览特性出现。

Java 14 有哪些新特性?

Java 14 有哪些新特性?

第14版包含的JEP(Java Enhancement Proposals,Java增强提案)比12版和13版加起来还要多。在这篇文章中,将主要讨论以下几点:

  • 改进的switch表达式,第一次出现在Java 12和13中,在Java 14中获得了完全的支持
  • instanceof支持模式匹配(语言特性)
  • NullPointerException(JVM特性)

希望你在阅读完本文后,积极地代码中实验这些功能,为Java团队提供反馈,并为Java的发展做出贡献。

一、Switch表达式

java 14 中的switch表达式将会永久存在。

在之前的发布中,switch表达式只是一个“预览”阶段的特性。我想提醒一下,“预览”阶段的特性的目的是为了收集反馈,这些特性可能会随时改变,根据反馈结果,这些特性甚至可能会被移除,但通常所有的预览特性最后都会在java中固定下来。

新的switch表达式的优点是,不再有缺省跳过行为(fall-through),更全面,而且表达式和组合形式更容易编写,因此出现bug的可能性就更低。例如,switch表达式现在可以使用箭头语法,如下所示:

var log = switch (event) {
    case PLAY -> "P";
    case STOP, PAUSE -> "SP";
    default -> {
        //获取 event转换成 string
        String message = event.toString();
        //获取 当前时间
        LocalDateTime now = LocalDateTime.now();
        //返回
        yield "Unknown event " + message +
              " logged on " + now;
    }
};
           

二、文本块

Java 13引入的一个预览功能是文本块。有了文本块,多行的字符串字面量就很容易编写了。

这个功能在Java 14中进行第二次预览,而且发生了一些变化。

例如,多行文本的格式化可能需要编写许多字符串连接操作和转义序列。下面的代码演示了一个HTML的例子:

String html = "<HTML>" +
"\n\t" + "<BODY>" +
"\n\t\t" + "<H1>\"Java 14 is here!\"</H1>" +
"\n\t" + "</BODY>" +
"\n" + "</HTML>";

           

有了文本块,就可以简化这一过程,只需使用三引号作为文本块的起始和结束标记,就能编写出更优雅的代码:

String html = """
<HTML>
  <BODY>
    <H1>"Java 14 is here!"</H1>
  </BODY>
</HTML>""";
           

与普通字符串字面量相比,文本块的表达性更好。

Java 14引入了两个新的转义序列。第一,可以使用新的 \s 转义序列来表示一个空格。第二,可以使用反斜杠 \ 来避免在行尾插入换行字符。这样可以很容易地在文本块中将一个很长的行分解成多行来增加可读性。

例如,现在编写多行字符串的方式如下:

String literal =
         "Lorem ipsum dolor sit amet, consectetur adipiscing " +
         "elit, sed do eiusmod tempor incididunt ut labore " +
         "et dolore magna aliqua.";
           

在文本块中使用 \ 转义序列,就可以写成这样:

String text = """
                Lorem ipsum dolor sit amet, consectetur adipiscing \
                elit, sed do eiusmod tempor incididunt ut labore \
                et dolore magna aliqua.\
                """;
                    
                    
    String text = """
                Lg
                ee
                e
                """;
		//长度为8,每个后面都有\n
        System.out.println(text.length());

        String text = """
                Lg\
                ee\
                e\
                """;
		//长度就是5
           

三、instanceof的模式匹配

Java 14引入了一个预览特性,有了它就不再需要编写先通过instanceof判断再强制转换的代码了。例如,下面的代码:

if (obj instanceof Group) {
  Group group = (Group) obj;

  // use group specific methods
  var entries = group.getEntries();
}
           

利用这个预览特性可以重构为:

if (obj instanceof Group group) {
  var entries = group.getEntries();
}
           

由于条件检查要求obj为Group类型,为什么还要像第一段代码那样在条件代码块中指明obj为Group类型呢?这可能会引发错误。

这种更简洁的语法可以去掉Java程序里的大多数强制类型转换。(2011年的一篇针对相关语言特性的研究论文(http://www.cs.williams.edu/FTfJP2011/6-Winther.pdf)指出,24%的类型转换都来自于instanceof后的条件语句。)

JEP 305解释了这项改变,并给出了Joshuoa Bloch的著作**《Effective Java》**中的一个例子,演示了下面两种等价的写法:

@Override public boolean equals(Object o) {
    return (o instanceof CaseInsensitiveString) &&
            ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}
           

这段代码中冗余的CaseInsensitiveString强制类型转换可以去掉,转换成下面的方式:

@Override public boolean equals(Object o) {
    return (o instanceof CaseInsensitiveString cis) &&
            cis.s.equalsIgnoreCase(s);
}
           

这个预览特性很值得尝试,因为它打开了通向更通用的模式匹配的大门。模式匹配的思想是为语言提供一个便捷的语法,根据特定的条件从对象中提取出组成部分。这正是instanceof操作符的用例,因为条件就是类型检查,提取操作需要调用适当的方法,或访问特定的字段。

换句话说,该预览功能仅仅是个开始,以后该功能肯定能够减少更多的代码冗余,从而降低bug发生的可能性。

四、Record

另一个预览功能就是record。与前面介绍的其他预览功能一样,这个预览功能也顺应了减少Java冗余代码的趋势,能帮助开发者写出更精准的代码。

  • Record主要用于特定领域的类,它的位移功能就是 存储数据,而没有任何自定义的行为。

我们开门见山,举一个最简单的领域类的例子:BankTransaction,它表示一次交易,

  • 包含三个字段:日期,金额,以及描述。定义类的时候需要考虑多个方面:

构造器

  1. getter方法
  2. toString()
  3. hashCode()和equals()

这些部分的代码通常由IDE自动生成,而且会占用很大篇幅。下面是生成的完整的BankTransaction类:

public class BankTransaction {
    //时间
    private final LocalDate date;
    //数量
    private final double amount;
    // desc 备注
    private final String description;

	//构造
    public BankTransaction(final LocalDate date,
                           final double amount,
                           final String description) {
        this.date = date;
        this.amount = amount;
        this.description = description;
    }
	//返回 date
    public LocalDate date() {
        return date;
    }
	//返回 amount
    public double amount() {
        return amount;
    }
	//返回 desc
    public String description() {
        return description;
    }
	
    //toString
    @Override
    public String toString() {
        return "BankTransaction{" +
                "date=" + date +
                ", amount=" + amount +
                ", description='" + description + '\'' +
                '}';
    }
	
    //equals
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BankTransaction that = (BankTransaction) o;
        return Double.compare(that.amount, amount) == 0 &&
                date.equals(that.date) &&
                description.equals(that.description);
    }
	//hashCode
    @Override
    public int hashCode() {
        return Objects.hash(date, amount, description);
    }
}
           

Java 14提供了一种方法可以解决这种冗余,可以更清晰地表达目的:这个类的唯一目的就是将数据整合在一起。Record会提供equals、hashCode和toString方法的实现。因此,BankTransaction类可以重构如下:

//idea 叫做录制
public record BankTransaction(LocalDate date,
                              double amount,
                              String description) {
    
}
           

通过record,可以“自动”地得到

  • equals,
  • hashCode
  • 和toString的实现,
  • 还有构造器和getter方法。

要想尝试这个例子,需要用preview标志编译该文件:

javac --enable-preview --release 14 BankTransaction.java

record的字段隐含为final。

因此,record的字段不能被重新赋值。

但要注意的是,这并不代表整个record是不可变的,保存在字段中的对象可以是可变的。

如果你有兴趣阅读更多关于record的内容,可以阅读Ben Evans最近在《Java Magazine》上发表的文章(https://blogs.oracle.com/javamagazine/records-come-to-java)。

请继续关注该功能。从培养新一代的Java开发者的视角来看,Record也很有意思。例如,如果你要培养初级开发者,那么record应该什么时候讲呢?是在讲OOP之前还是之后?

五、NullPointerException

些人认为,抛出NullPointerException异常应该当做新的“Hello World”程序来看待,因为NullPointerException是早晚会遇到的。玩笑归玩笑,这个异常的确会造成困扰,因为它经常出现在生产环境的日志中,会导致调试非常困难,因为它并不会显示原始的代码。例如,如下代码:

在Java 14之前,你可能会得到如下的错误:

Exception in thread "main" java.lang.NullPointerException
    at NullPointerExample.main(NullPointerExample.java:5)
           

不幸的是,如果在第5行是一个包含了多个方法调用的赋值语句(如getLocation()和getCity()),那么任何一个都可能会返回null。实际上,变量user也可能是null。因此,无法判断是谁导致了NullPointerException。

在Java 14中,新的JVM特性可以显示更详细的诊断信息:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Location.getCity()" because the return value of "User.getLocation()" is null
    at NullPointerExample.main(NullPointerExample.java:5)
           

该消息包含两个明确的组成部分

  • 后果:Location.getCity()无法被调用
  • 原因:User.getLocation()的返回值为null

增强版本的诊断信息只有在使用下述标志运行Java时才有效:

-XX:+ShowCodeDetailsInExceptionMessages

下面是个例子:

java -XX:+ShowCodeDetailsInExceptionMessages NullPointerExample

在以后的版本中,该选项可能会成为默认。

这项改进不仅对于方法调用有效,其他可能会导致NullPointerException的地方也有效,包括字段访问、数组访问、赋值等。

六、总结

Java 14 提供了几个新的预览版语言特性和更新,能很好地帮助开发者完成日常工作。

  • Java 14 还引入了record,这是一种创建 精确数据类的 新方法。
  • 此外,NullPointerException的消息经过了改进,能显示明确的诊断信息。
  • switch表达式也成了Java 14的一部分。文本块功能可以帮你处理多行字符串,这是在引入了两个新的转义序列之后的另一预览功能。
  • 还有一项改动就是JDK Flight Recorder的事件流。