java新特性

JDK14新特性介绍

2020-03-21  本文已影响0人  spt_genius

JDK14新特性介绍


描述

OpenJDK14是Java SE平台的版本14的开源参考实现,由Java社区流程中的JSR 389指定 。2020年3月17日JDK14达到一般可用性, 基于GPL协议的构建许可文件可从Oracle获得; 其他供应商的二进制文件将在不久之后。已通过JEP 2.0提案修订的JEP流程提出并跟踪了此版本的功能和时间表。该发行版是使用JDK发行流程(JEP 3)制作的。

此次的更新包含了很多令人兴奋的新功能。总共有非常令人印象深刻的16个JDK增强建议(JEP)和69个新的API元素。


新功能

Records

Java是一种面向对象的语言。您可以创建类来保存数据,并使用封装来控制如何访问和修改该数据。对象的使用使操作复杂的数据类型变得简单而直接。这是Java如此流行作为平台的原因之一。缺点(到现在为止)是,创建数据类型非常冗长,即使在最直接的情况下也需要大量代码。让我们看一下基本二维点所需的代码:

public class Point {
  private final double x;
  private final double y;

  public Point(double x, double y) {
    this.x = x;
    this.y = y;
  }

  public double getX() {
    return x;
  }

  public double getY() {
    return y;
  }
}

这14行代码仅仅表示了2个值元。

JDK 14引入了记录(records)作为预览功能。预览功能是一个新概念,使Java平台的开发人员可以在不使其成为Java SE标准一部分的情况下包括新的语言功能。通过这样做,开发人员可以尝试这些功能,并提供反馈,以便在必要时在标准中设置功能之前进行更改(甚至删除功能)。要使用预览功能,必须为编译和运行时指定命令行标志--enable-preview。对于编译,还必须指定-source标志。

记录是表示数据类的一种简单得多的方法。如果以Point为例,代码可以简化为一行:

public record Point(double x, double y) { }

这与代码的可读性无关。我们立即意识到,我们现在有了一个包含两个称为x和y的双精度值的类,我们可以使用getX和getY的标准方法名称进行访问。

让我们检查一下记录的一些细节。

首先,记录是一种新的类型,它是类的受限形式,就像枚举一样。记录具有名称和状态描述,用于定义记录的组成部分。在上面的Point示例中,状态描述是double和x和y。记录是为了简化而设计的,因此它们不能扩展任何其他类或定义其他实例变量。记录中的所有状态都是最终状态,因此不提供set方法。如果您需要任何一个,则需要使用成熟的类。

记录确实具有灵活性。

通常,构造函数除了提供值外还需要提供其他行为。如果是这种情况,我们可以提供构造函数的替代实现:

record Range(int min, int max) {
  Range {
    if (min > max)
      throw new IllegalArgumentException(“Max must be >= min”);
  }
}

请注意,由于指定参数是多余的,因此构造方法的定义仍被缩写。也可以声明从状态描述自动派生的任何成员,因此,例如,您可以提供toString ()或hashCode ()实现的替代方法。

instanceof

在某些情况下,您不知道对象的确切类型。为了解决这个问题,Java有instanceof运算符,可用于针对不同类型进行测试。这样做的缺点是确定了对象的类型。如果要使用显式类型转换,则必须使用显式类型转换:

if (o instanceof String) {
  String s = (String)o;
  System.out.println(s.length);
}

在JDK 14中,对instanceof运算符进行了扩展,以允许除了类型之外还指定变量名。然后可以使用该变量而无需显式强制转换:

if (o instanceof String s)
  System.out.println(s.length);

变量的范围限于在逻辑上正确使用的地方,因此:

if (o instanceof String s)
  System.out.println(s.length);
else
  // s在这里超出范围

该范围也可以在条件语句中应用,因此我们可以执行以下操作:

if (o instanceof String s && s.length() > 4) ...

这是有意义的,因为length()方法将只是被称为如果 o为一个字符串。逻辑或运算符不一样:

if (o instanceof String s || s.length() > 4) ...

在这种情况下,无论o是否为String的结果,都需要对s.length()进行求值。从逻辑上讲,这不起作用,因此会导致编译错误。

使用逻辑非运算符可以产生一些有趣的作用域效果:

if (!(o instanceof String s && s.length() > 3)
  return;
System.out.println(s.length());  // s 在这里

我已经看到了对变量的范围划分方式的一些负面反馈,但是,鉴于所有作用域都是完全合乎逻辑的,因此我认为它非常有效。

已经有计划提供此功能的第二个预览版本,该版本将扩展模式匹配以与记录一起使用,并提供实现解构模式的简单方法。可以在JEP 375中找到更多详细信息。

有帮助的NullPointException

任何编写了多行Java代码的人都将在某个时候遇到NullPointerException。未能初始化对象引用(或将其错误地显式设置为null),然后尝试使用该引用将导致引发此异常。

在简单的情况下,找出问题的原因很简单。如果我们尝试运行以下代码:

public class NullTest {
  List<String> list;
  public NullTest() {
    list.add("foo");
  }
}

生成的错误是:

Exception in thread "main" java.lang.NullPointerException
            at jdk14.NullTest.<init>(NullTest.java:16)
            at jdk14.Main.main(Main.java:15)

由于我们在第16行上引用列表,因此很明显,列表是罪魁祸首,我们可以快速解决问题。
但是,如果我们在这样的行中使用链接引用:

a.b.c.d = 12;

运行此命令时,我们可能会看到如下错误:

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

问题在于我们无法由此确定异常是由于a为null,b为null还是c为null的结果。我们要么需要使用IDE中的调试器,要么更改代码,将引用分隔到不同的行上。两者都不是理想的。

在JDK 14中,如果我们运行相同的代码,我们将看到类似以下内容:

Exception in thread "main" java.lang.NullPointerException:
    Cannot read field "c" because "a.b" is null
    at Prog.main(Prog.java:5)

马上,我们可以看到ab是问题所在,并着手进行纠正。我敢肯定,这将使许多Java开发人员的生活更加轻松,绝对拍手叫好的,更好的定位才是解决bug的最佳途径。


新API

java.io

PrintStream有两个新方法,write(byte [] buf)和writeBytes(byte [] buf)。这些有效地做同样的事情,等效于write(buf,0,buf.length)。之所以拥有两种不同的方法,是因为将write定义为抛出IOException(但是,奇怪的是,从不抛出),而writeBytes没有。因此,选择使用哪种方法取决于您是否希望使用try-catch块包围该调用。

有一个新的注释类型,Serial。它旨在用于对序列化的编译器检查。特别是,此类型的注释应应用于与序列化相关的方法和声明为可序列化的类中的字段。(它在某些方面与Override注释类似)。

java.lang

Class类为新的Record功能提供了两种方法:isRecord()getRecordComponents()getRecordComponents()方法返回一个RecordComponent对象数组。RecordComponentjava.lang.reflect包中的新类,它具有11种方法来检索内容,例如注释的详细信息和泛型。

Record是一个简单的新类,它重写Object的equals,hashCode和toString方法。

NullPointerException现在作为有帮助的NullPointerExceptions功能的一部分覆盖了Throwable中的getMessage方法。

StrictMath类具有六个新方法,这些方法补充了需要检测溢出错误时使用的现有精确方法。新方法是decrementExact,incrementExact和negateExact(所有方法都有int和long参数的两个重载版本)。

java.lang.annotation

ElementType枚举为Records添加了一个新的常量RECORD_TYPE。

java.lang.invoke

MethodHandles.Lookup类具有两个新方法:

java.lang.runtime

这是JDK 14中的新软件包,只有一个类ObjectMethods。这是记录功能的低级部分,它具有单个方法(引导程序),该方法生成对象等于,hashCode和toString方法。

java.util.text

CompactNumberFormat类具有一个新的构造函数,该构造函数为pluralRules添加了一个参数。这是一个String,表示将Count关键字(例如“ one”)与实际整数关联的多个规则。它的语法在Unicode Consortium的复数规则语法中定义。

java.util

HashSet类具有一个新方法toArray,该方法返回一个数组,其运行时组件类型为Object,包含此集合中的所有元素。

java.util.concurrent.locks

LockSupport类具有一个新方法setCurrentBlocker。LockSupport提供了暂存和取消暂存线程的功能(与不赞成使用的Thread.suspend和Thread.resume方法没有相同的问题)。现在可以设置getBlocker将返回的Object。从非公共对象调用无参数方法时,这很有用。

javax.lang.model.element

ElementKind枚举具有三个新常量,用于记录和模式匹配实例的特征,即BINDING_VARIABLERECORDRECORD_COMPONENT

javax.lang.model.util

该软件包提供实用程序,以协助处理程序元素和类型。通过添加记录,添加了一组新的抽象类和具体类来支持此功能。(示例是AbstractTypeVisitor14,ElementScanner14和TypeKindVisitor14)。

org.xml.sax

一种新方法已添加到SAX XML解析器的ContentHandler接口。声明方法接收XML声明的通知。在默认实现的情况下,此操作无效。

JEP 370:外部内存访问API

这是作为孵化器模块引入的,以允许更广泛的Java社区进行测试,并在其成为Java SE标准的一部分之前集成反馈。它旨在替代sun.misc.Unsafejava.io.MappedByteBuffer

外部存储器访问API引入了三个主要抽象:


JVM改变


其它功能

有许多与OpenJDK的不同部分相关的JEP:

上一篇下一篇

猜你喜欢

热点阅读