What's new from Java?

java各版本文档官网直达链接

java14

官网直达链接

一、switch表达式(标准版)

switch表达式在java12与java13中进行了两次增强,经过测试之后,在java14中此修改已经正式转正。

详情请看本文上方java12与java13中的内容,或见java12 jep325java13 jep354

二、instanceof 的模式匹配(预览版)

在此前,我们使用instanceof 来判断一个对象是否属于某个指定的类或其子类的实例,如果是,我们再进行转换,如:

if (obj instanceof String) {
    String s = (String) obj;
    // use s
}

在上方代码可以看出,我们总共进行了三步:

  1. obj对象是否为String类?
  2. obj转为String
  3. 新的局部变量s的声明

这种模式很简单,所有 Java 程序员都能理解,但由于几个原因,它不是最理想的。第一步为true之后第二步显得有点多此一举。因为类型匹配了之后下一步往往都是类型转换了。于是有了java14中的新语法:

//如果obj为String类型,那么可以直接在if语句中转换并定义为s
if (obj instanceof String s) {
    // can use s here
} else {
    // can't use s here
}

三、java打包工具

jep343

许多 Java 应用程序需要以一流的方式安装在本机平台上,而不是简单地放在类路径或模块路径上。应用程序开发人员仅提供一个简单的 JAR 文件是不够的;他们必须提供适合本机平台的可安装包。这允许以用户熟悉的方式分发、安装和卸载 Java 应用程序。

java14中引入了jpackage工具,使用 jpackage 命令可以把 JAR 包打包成不同操作系统支持的软件格式。

常见平台的打包格式如下:

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

四、更有用的NullPointerExceptions

通过准确描述哪个变量为空来提高 JVM 生成的 NullPointerExceptions 的可用性。

在java14之前,如果在一行代码中有多个表达式时出现了 NullPointerExceptions 的话我们无法确定到底是哪个对象为 null。在java14,现在会直接的告诉你哪个对象为null。如下:

public static void main(String[] args) {
    String s1 = "string1";
    String s2 = null;
    int countLength = s1.length() + s2.length();
    System.out.println(countLength);
}

java14前报错如下:

Exception in thread "main" java.lang.NullPointerException
	at com.zzl.jdk14.NullPointerTest.main(NullPointerTest.java:12)

java14报错

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "s2" is null
	at com.zzl.jdk14.NullPointerTest.main(NullPointerTest.java:12)

打印详细错误信息命令(默认为不开启):

-XX:{+|-}ShowCodeDetailsInExceptionMessages

五、字符串文本块(二次预览版)

字符串文本块是java13新增的功能,详情请看上方。

在本次更新中,新增了两个转义符:

  1. \:表示结尾不换行
  2. \s:表示一个空格

示例:

String text = """
              Lorem ipsum dolor sit amet, consectetur adipiscing \
              elit, sed do eiusmod tempor incididunt ut labore \
              et\sdolore\smagna aliqua.\
              """;
                  
//输出
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labor et dolore magna aliqua.

六、Record(预览版)

Record是 Java 语言中一种新的类型声明。像枚举一样,Record是类的受限形式。Record放弃了类通常享有的自由:将 API 与表示分离的能力。作为回报,Records获得了相当程度的简洁性。

定义一个Record:

record Point(int x, int y) { }

因为Records的语义声称是简单、透明的数据持有者,所以Records会默认存在许多特性:

  • 所有属性都是 final
  • 会自动创建一个 public 的构造方法,用于初始化所有属性
  • 会自动构建 publicequalshashCode 方法,它们的实现表示如果两条记录具有相同的类型并且包含相同的状态,则它们是相等的
  • 会自动构建 publictoString 方法,包括所有记录组件的字符串表示形式及其名称

Class类新增反射API:

  • RecordComponent[] getRecordComponents()

    java.lang.reflect.RecordComponent 是一个新类。此数组的元素对应于记录的组件,其顺序与它们在记录声明中出现的顺序相同。可以从 RecordComponent 数组中的每一个中提取附加信息,包括其名称、类型、泛型类型、注释及其访问器方法。

  • boolean isRecord() 如果给定的类被声明为 Record,则该方法返回 true

七、JFR事件流

公开 JDK Flight Recorder 数据以进行持续监控。HotSpot VM 使用 JFR 发出超过 500 个数据点,其中大部分无法通过解析日志文件之外的其他方式获得。模块jdk.jfr 中的包 jdk.jfr.consumer 扩展了异步订阅事件的功能。用户可以直接从磁盘存储库中读取录制数据或流式传输,而无需转储录制文件。

基本示例:

public static void main(String[] args) throws IOException, ParseException {
    Configuration c = Configuration.getConfiguration("default");
    try (var rs = new RecordingStream(c)) {
        //打印GC信息
        rs.onEvent("jdk.GarbageCollection", System.out::println);
        //打印CPU负载
        rs.onEvent("jdk.CPULoad", System.out::println);
        //打印JVM信息
        rs.onEvent("jdk.JVMInformation", System.out::println);
        rs.start();
    }
}

八、其它

  • G1 的 NUMA 感知内存分配

    通过实施 NUMA 感知内存分配来提高大型机器上的 G1 性能。

    通过 +XX:+UseNUMA 参数来启用此功能。

  • macOS上的ZGC(实验性)

    将 ZGC 垃圾收集器移植到 macOS。

    通过 -XX:+UnlockExperimentalVMOptions -XX:+UseZGC 参数来在macOS中启用。

  • Windows上的ZGC(实验性)。

    将 ZGC 垃圾收集器移植到 Windows。

    通过 -XX:+UnlockExperimentalVMOptions -XX:+UseZGC 参数来在Windows中启用。

java15

官网直达链接

一、密封类(预览版)

使用密封的类和接口增强 Java 编程语言。密封的类和接口限制了哪些其他类或接口可以扩展或实现它们。

在 Java 中,类层次结构允许通过继承重用代码:超类的方法可以被许多子类继承(并因此重用)。

在此之前,在这方面提供了有限的工具:要么创建一个类 final,因此它有零个子类,要么将一个类或其构造函数设为包私有,因此它只能在同一个包中具有子类。如:

package java.lang;

abstract class AbstractStringBuilder {...}
public final class StringBuffer  extends AbstractStringBuilder {...}
public final class StringBuilder extends AbstractStringBuilder {...}

在java15开始,可以通过将 sealed 修饰符应用于其声明来密封类,用 permits 关键字来指定哪些类可以继承、实现此类,密封的类或接口只能由允许这样做的类和接口扩展或实现。如:

package com.example.geometry;

public abstract sealed class Shape
    permits Circle, Rectangle, Square {...}

permits 指定的类必须位于超类附近:在同一个模块中(如果超类在命名模块中)或在同一个包中(如果超类在未命名模块中)。子类继承、实现父密封类之后,每个允许的子类必须使用一个且只有一个修饰符 finalsealednon-sealed

  • final:最终类,不能再有子类
  • sealed:密封类,可以指定允许继承的类
  • non-sealed:非密封类,允许所有类继承

示例如下:

package com.example.geometry;

//定义一个Shape的抽象密封类
public abstract sealed class Shape
    permits Circle, Rectangle, Square {...}

//定义一个最终的Circle类并继承Shape,Circle将不能再有子类
public final class Circle extends Shape {...}

//定义了一个继承Shape的Rectangle密封类,并指定了TransparentRectangle、FilledRectangle才能够继承它
public sealed class Rectangle extends Shape 
    permits TransparentRectangle, FilledRectangle {...}
public final class TransparentRectangle extends Rectangle {...}
public final class FilledRectangle extends Rectangle {...}

//定义了一个非密封的Square,它可以被任意类继承
public non-sealed class Square extends Shape {...}

二、隐藏类

隐藏类,即不能被其他类的字节码直接使用的类。隐藏类旨在供在运行时生成类并通过反射间接使用它们的框架使用。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类卸载。

许多基于 JVM 的语言实现依赖于动态类生成来实现灵活性和效率。例如,在 Java 语言的情况下,javac 不会class 在编译时将 lambda 表达式转换为专用文件,而是发出字节码,动态生成并实例化一个类,以便在需要时生成与 lambda 表达式对应的对象。类似地,非 Java 语言的运行时通常通过使用动态代理来实现这些语言的高阶特性,动态代理也动态生成类。

基本示例:

/**
 * 提前准备一个类,并编译成为字节码
 */
public class HideClass {

    public static void test(){
        System.out.println("HideClass::test");
    }
}

public class HideClassTest {

    public static void main(String[] args) throws Throwable {
        //加载编译后的字节码
        FileInputStream inputStream = new FileInputStream("E:\\HideClass.class");
        byte[] bytes = inputStream.readAllBytes();
        //从字节数组加载类
        Class<?> proxy = MethodHandles.lookup().defineHiddenClass(bytes, true, MethodHandles.Lookup.ClassOption.NESTMATE).lookupClass();
        //打印类名
        System.out.println(proxy.getName());
        //获取HideClass的test方法
        MethodHandle methodHandle = MethodHandles.lookup().bind(proxy.getConstructor().newInstance(), "test", MethodType.methodType(Void.TYPE));
        //反射调用test方法
        methodHandle.invokeExact();
    }
}

//输出
com.zzl.jdk15.HideClass/0x0000000800bb2040
HideClass::test

三、Record(二次预览)

记录是 Java 语言中的一种新类。记录的目的是声明一小组变量将被视为一种新的实体。在java14中上线预览,详情看上方java14。

在java15中,Record进行了增强:

  1. Record支持密闭类型

    package com.example.expression;
    
    public sealed interface Expr
        permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...}
    
    public record ConstantExpr(int i)       implements Expr {...}
    public record PlusExpr(Expr a, Expr b)  implements Expr {...}
    public record TimesExpr(Expr a, Expr b) implements Expr {...}
    public record NegExpr(Expr e)           implements Expr {...}
    
  2. 支持注解,如:

    public final class Card {
        private final @MyAnno Rank rank;
        private final @MyAnno Suit suit;
        @MyAnno Rank rank() { return this.rank; }
        @MyAnno Suit suit() { return this.suit; }
        ...
    }
    

    可以转为更简洁中的Record如下:

    public record Card(@MyAnno Rank rank, @MyAnno Suit suit) { ... }
    

    注意:

    • 如果Record组件上的注解适用于字段声明,则注释出现在相应的 private 字段上
    • 如果Record组件上的注解适用于方法声明,则该注释出现在相应的访问器方法上
    • 如果Record组件上的注解适用于形式参数,则注释出现在规范构造函数的相应形式参数(如果未显式声明)或紧凑构造函数的相应形式参数(如果显式声明)
    • 如果Record组件上的注解适用于类型,则传播规则与声明注释相同,只是注解出现在相应的类型使用而不是声明上

更多详情

四、其它

  • 爱德华兹曲线数字签名算法

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
        KeyPair kp = kpg.generateKeyPair();
        byte[] msg = "www.zzlcjj.xyz".getBytes(StandardCharsets.UTF_8);
        Signature sig = Signature.getInstance("Ed25519");
        sig.initSign(kp.getPrivate());
        sig.update(msg);
        byte[] s = sig.sign();
        System.out.println(Base64.getEncoder().encodeToString(s));
    }
    
  • 重新实现旧的 DatagramSocket API

  • 移除 Nashorn JavaScript 引擎

  • 弃用和禁用偏向锁定

    jep374

  • ZGC:可扩展的低延迟垃圾收集器(生产)

    将 Z 垃圾收集器从实验功能更改为产品功能。

    通过 -XX:+UseZGC 命令行选项启用

  • Shenandoah:低暂停时间垃圾收集器(生产)

    通过 -XX:+UseShenandoahGC 命令行选项启用

  • 删除 Solaris 和 SPARC 端口

    jep381

  • 支持Unicode 13.0

  • 新增CharSequence类的 isEmpty() 默认方法,用于判断字符串序列是否为空

java16

官网直达链接