换掉 Java 8 !Java 17&18 新特性真香

Posted Hollis Chuang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了换掉 Java 8 !Java 17&18 新特性真香相关的知识,希望对你有一定的参考价值。

来源于程序猿阿朗 ,作者程序猿阿朗

Hollis的新书限时折扣中,一本深入讲解Java基础的干货笔记!

Java 17

img

Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个长期支持(LTS)版本,这次更新共带来 14 个新功能。

OpenJDK Java 17 下载:https://jdk.java.net/archive/

OpenJDK Java 17 文档:https://openjdk.java.net/projects/jdk/17/

JEP描述
JEP 306恢复始终严格的浮点语义[1]
JEP 356增强的伪随机数生成器[2]
JEP 382使用新的 macOS 渲染库[3]
JEP 391支持 macOS/AArch64 架构[4]
JEP 398删除已启用的 Applet API[5]
JEP 403更强的封装 JDK 内部封装[6]
JEP 406Switch 模式匹配(预览)[7]
JEP 407移除 RMI Activation[8]
JEP 409密封类(Sealed Classes)[9]
JEP 410JEP 401:移除实验性的 AOT 和 JIT 编译器[10]
JEP 411弃用 Security Manager[11]
JEP 412外部函数和内存 API(孵化器)[12]
JEP 414Vector API(第二孵化器)[13]
JEP 415指定上下文的反序列化过滤器[14]

JEP 306:恢复始终严格的浮点语义

既然是恢复严格的浮点语义,那么说明在某个时间点之前,是始终严格的浮点语义的。其实在 Java SE 1.2 之前,所有的浮点计算都是严格的,但是以当初的情况来看,过于严格的浮点计算在当初流行的 x86 架构和 x87 浮点协议处理器上运行,需要大量的额外的指令开销,所以在 Java SE 1.2 开始,需要手动使用关键字 strictfp(strict float point) 才能启用严格的浮点计算。

但是在 2021 年的今天,硬件早已发生巨变,当初的问题已经不存在了,所以从 Java 17 开始,恢复了始终严格的浮点语义这一特性。

扩展strictfp 是 Java 中的一个关键字,大多数人可能没有注意过它,它可以用在类、接口或者方法上,被 strictfp 修饰的部分中的 float 和 double 表达式会进行严格浮点计算。

下面是一个示例,其中的 testStrictfp() 被 strictfp 修饰。

package com.wdbyte;

public class Main 
    public static void main(String[] args) 
        testStrictfp();
    

    public strictfp static void testStrictfp() 
        float aFloat = 0.6666666666666666666f;
        double aDouble = 0.88888888888888888d;
        double sum = aFloat + aDouble;
        System.out.println("sum: " + sum);
    

JEP 356:增强的伪随机数生成器

为伪随机数生成器 RPNG(pseudorandom number generator)增加了新的接口类型和实现,让在代码中使用各种 PRNG 算法变得容易许多。

这次增加了 RandomGenerator 接口,为所有的 PRNG 算法提供统一的 API,并且可以获取不同类型的 PRNG 对象流。同时也提供了一个新类 RandomGeneratorFactory 用于构造各种 RandomGenerator 实例,在 RandomGeneratorFactory 中使用 ServiceLoader.provider 来加载各种 PRNG 实现。

下面是一个使用示例:随便选择一个 PRNG 算法生成 5 个 10 以内的随机数。

package com.wdbyte.java17;

import java.util.Date;
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
import java.util.stream.Stream;

/**
 * @author niulang
 */
public class JEP356 

    public static void main(String[] args) 
        RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
        // 使用时间戳作为随机数种子
        RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
        for (int i = 0; i < 5; i++) 
            System.out.println(randomGenerator.nextInt(10));
        
    

得到输出:

7
3
4
4
6

你也可以遍历出所有的 PRNG 算法。

RandomGeneratorFactory.all().forEach(factory -> 
    System.out.println(factory.group() + ":" + factory.name());
);

得到输出:

LXM:L32X64MixRandom
LXM:L128X128MixRandom
LXM:L64X128MixRandom
Legacy:SecureRandom
LXM:L128X1024MixRandom
LXM:L64X128StarStarRandom
Xoshiro:Xoshiro256PlusPlus
LXM:L64X256MixRandom
Legacy:Random
Xoroshiro:Xoroshiro128PlusPlus
LXM:L128X256MixRandom
Legacy:SplittableRandom
LXM:L64X1024MixRandom

可以看到 Legacy:Random 也在其中,新的 API 兼容了老的 Random 方式,所以你也可以使用新的 API 调用 Random 类生成随机数。

// 使用 Random
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("Random");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
for (int i = 0; i < 5; i++) 
    System.out.println(randomGenerator.nextInt(10));

扩展阅读:增强的伪随机数生成器[15]

JEP 382:使用新的 macOS 渲染库

macOS 为了提高图形的渲染性能,在 2018 年 9 月抛弃了之前的 OpenGL 渲染库 ,而使用了 Apple Metal 进行代替。Java 17 这次更新开始支持 Apple Metal,不过对于 API 没有任何改变,这一些都是内部修改。

扩展阅读:macOS Mojave 10.14 Release Notes[16],Apple Metal[17]

JEP 391:支持 macOS/AArch64 架构

起因是 Apple 在 2020 年 6 月的 WWDC 演讲中宣布,将开启一项长期的将 Macintosh 计算机系列从 x64 过度到 AArch64 的长期计划,因此需要尽快的让 JDK 支持 macOS/AArch64 。

Linux 上的 AArch64 支持以及在 Java 16 时已经支持,可以查看之前的文章了解。

扩展:Java 16 新功能介绍 - JEP 386[18]

JEP 398:删除已弃用的 Applet API

Applet 是使用 Java 编写的可以嵌入到 html 中的小应用程序,嵌入方式是通过普通的 HTML 标记语法,由于早已过时,几乎没有场景在使用了。

示例:嵌入 Hello.class

<applet code="Hello.class" height=200 width=200></applet>

Applet API 在 Java 9 时已经标记了废弃,现在 Java 17 中将彻底删除。

JEP 403:更强的 JDK 内部封装

如 Java 16 的 JEP 396 中描述的一样,为了提高 JDK 的安全性,使 --illegal-access 选项的默认模式从允许更改为拒绝。通过此更改,JDK 的内部包和 API(关键内部 API[19] 除外)将不再默认打开。

但是在 Java 17 中,除了 sun.misc.Unsafe ,使用 --illegal-access 命令也不能打开 JDK 内部的强封装模式了,除了 sun.misc.Unsafe API .

在 Java 17 中使用 --illegal-access 选项将会得到一个命令已经移除的警告。

➜  bin ./java -version
openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)
➜  bin ./java --illegal-access=warn
OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0

扩展阅读:JEP 403:更强的 JDK 内部封装[20],Java 16 新功能介绍[21]

JEP 406:switch 的类型匹配(预览)

如 instanceof 一样,为 switch 也增加了类型匹配自动转换功能。

在之前,使用 instanceof 需要如下操作:

if (obj instanceof String) 
    String s = (String) obj;    // grr...
    ...

多余的类型强制转换,而现在:

if (obj instanceof String s) 
    // Let pattern matching do the work!
    ...

switch 也可以使用类似的方式了。

static String formatterPatternSwitch(Object o) 
    return switch (o) 
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    ;

对于 null 值的判断也有了新的方式。

// Java 17 之前
static void testFooBar(String s) 
    if (s == null) 
        System.out.println("oops!");
        return;
    
    switch (s) 
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    

// Java 17
static void testFooBar(String s) 
    switch (s) 
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    

扩展阅读:JEP 406:switch 的类型匹配(预览)[22]

JEP 407:移除 RMI Activation

移除了在 JEP 385 中被标记废除的 RMI(Remote Method Invocation)Activation,但是 RMI 其他部分不会受影响。

RMI Activation 在 Java 15 中的 JEP 385 已经被标记为过时废弃,至今没有收到不良反馈,因此决定在 Java 17 中正式移除。

扩展阅读:JEP 407:移除 RMI Activation[23]

JEP 409:密封类(Sealed Classes)

Sealed Classes 在 Java 15 中的 JEP 360 中提出,在 Java 16 中的 JEP 397 再次预览,现在 Java 17 中成为正式的功能,相比 Java 16 并没有功能变化,这里不再重复介绍,想了解的可以参考之前文章。

扩展阅读:Java 16 新功能介绍[24],JEP 409: Sealed Classes[25]

JEP 401:移除实验性的 AOT 和 JIT 编译器

在 Java 9 的 JEP 295 中,引入了实验性的提前编译 jaotc 工具,但是这个特性自从引入依赖用处都不太大,而且需要大量的维护工作,所以在 Java 17 中决定删除这个特性。

主要移除了三个 JDK 模块:

  1. jdk.aot - jaotc 工具。

  2. Jdk.internal.vm.compiler - Graal 编译器。

  3. jdk.internal.vm.compiler.management

同时也移除了部分与 AOT 编译相关的 HotSpot 代码:

  1. src/hotspot/share/aot — dumps and loads AOT code

  2. Additional code guarded by #if INCLUDE_AOT

JEP 411:弃用 Security Manager

Security Manager 在 JDK 1.0 时就已经引入,但是它一直都不是保护服务端以及客户端 Java 代码的主要手段,为了 Java 的继续发展,决定弃用 Security Manager,在不久的未来进行删除。

@Deprecated(since="17", forRemoval=true)
public class SecurityManager 
 // ...

JEP 412:外部函数和内存 API (孵化)

新的 API 允许 Java 开发者与 JVM 之外的代码和数据进行交互,通过调用外部函数,可以在不使用 JNI 的情况下调用本地库。

这是一个孵化功能;需要添加--add-modules jdk.incubator.foreign来编译和运行 Java 代码。

历史

  • Java 14 JEP 370[26]引入了外部内存访问 API(孵化器)。

  • Java 15 JEP 383[27]引入了外部内存访问 API(第二孵化器)。

  • Java 16 JEP 389[28]引入了外部链接器 API(孵化器)。

  • Java 16 JEP 393[29]引入了外部内存访问 API(第三孵化器)。

  • Java 17 JEP 412[30]引入了外部函数和内存 API(孵化器)。

扩展阅读:JEP 412:外部函数和内存 API (孵化)[31]

JEP 414:Vector API(二次孵化)

在 Java 16 中引入一个新的 API 来进行向量计算,它可以在运行时可靠的编译为支持的 CPU 架构,从而实现更优的计算能力。

现在 Java 17 中改进了 Vector API 性能,增强了例如对字符的操作、字节向量与布尔数组之间的相互转换等功能。

JEP 415:指定上下文的反序列化过滤器

Java 中的序列化一直都是非常重要的功能,如果没有序列化功能,Java 可能都不会占据开发语言的主导地位,序列化让远程处理变得容易和透明,同时也促进了 Java EE 的成功。

但是 Java 序列化的问题也很多,它几乎会犯下所有的可以想象的错误,为开发者带来持续的维护工作。但是要说明的是序列化的概念是没有错的,把对象转换为可以在 JVM 之间自由传输,并且可以在另一端重新构建的能力是完全合理的想法,问题在于 Java 中的序列化设计存在风险,以至于爆出过很多和序列化相关的漏洞。

反序列化危险的一个原因是,有时候我们不好验证将要进行反序列化的内容是否存在风险,而传入的数据流可以自由引用对象,很有可能这个数据流就是攻击者精心构造的恶意代码。

所以,JEP 415 允许在反序列化时,通过一个过滤配置,来告知本次反序列化允许或者禁止操作的类,反序列化时碰到被禁止的类,则会反序列化失败。

反序列化示例

假设 Dog 类中的 Poc 是恶意构造的类,但是正常反序列化是可以成功的。

package com.wdbyte.java17;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author niulang
 */
public class JEP415 
    public static void main(String[] args) throws IOException, ClassNotFoundException 
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 对象转字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) 
            objectOutputStream.writeObject(dog);
        
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字节数组转对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
    


class Dog implements Serializable 
    private String name;
    private Poc poc;

    public Dog(String name) 
        this.name = name;
    

    @Override
    public String toString() 
        return "Dog" + "name='" + name + '\\'' + '';
    
  // get...set...


class Poc implements Serializable

输出结果:

Dogname='哈士奇'

反序列化过滤器

在 Java 17 中可以自定义反序列化过滤器,拦截不允许的类。

package com.wdbyte.java17;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputFilter;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author niulang
 */
public class JEP415 
    public static void main(String[] args) throws IOException, ClassNotFoundException 
        Dog dog = new Dog("哈士奇");
        dog.setPoc(new Poc());
        // 序列化 - 对象转字节数组
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) 
            objectOutputStream.writeObject(dog);
        
        byte[] bytes = byteArrayOutputStream.toByteArray();
        // 反序列化 - 字节数组转对象
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        // 允许 com.wdbyte.java17.Dog 类,允许 java.base 中的所有类,拒绝其他任何类
        ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
                        "com.wdbyte.java17.Dog;java.base/*;!*");
        objectInputStream.setObjectInputFilter(filter);
        Object object = objectInputStream.readObject();
        System.out.println(object.toString());
    


class Dog implements Serializable 
    private String name;
    private Poc poc;

    public Dog(String name) 
        this.name = name;
    

    @Override
    public String toString() 
        return "Dog" + "name='" + name + '\\'' + '';
    
  // get...set...


class Poc implements Serializable

这时反序列化会得到异常。

Exception in thread "main" java.io.InvalidClassException: filter status: REJECTED
 at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1412)
 at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2053)
 at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1907)
 ....

扩展阅读:JEP 415:指定上下文的反序列化过滤器[32]

Java 18

Java 18 在 2022 年 3 月 22 日正式发布,这次更新共带来 9 个新功能。

Java 18 不是一个长期支持版本,如果你想要升级自己的 JDK 版本的话,我会更推荐 Java17。目前来看,迁移到 Java17 的项目越来越多,尤其是在国外。

OpenJDK Java 18 下载:https://jdk.java.net/18/

OpenJDK Java 18 文档:https://openjdk.java.net/projects/jdk/18/

JEP描述
JEP 400默认为 UTF-8[33]
JEP 408简单的网络服务器[34]
JEP 413Java API 文档中的代码片段[35]
JEP 416使用方法句柄重新实现核心反射[36]
JEP 417Vector API(三次孵化)[37]
JEP 418互联网地址解析 SPI[38]
JEP 419Foreign Function & Memory API (二次孵化)[39]
JEP 420switch 模式匹配(二次预览)[40]
JEP 421弃用完成删除[41]

JEP 400:默认 UTF-8 字符编码

JDK 一直都是支持 UTF-8 字符编码,这次是把 UTF-8 设置为了默认编码,也就是在不加任何指定的情况下,默认所有需要用到编码的 JDK API 都使用 UTF-8 编码,这样就可以避免因为不同系统,不同地区,不同环境之间产生的编码问题。

Mac OS 默认使用 UTF-8 作为默认编码,但是其他操作系统上,编码可能取决于系统的配置或者所在区域的设置。如中国大陆的 windows 使用 GBK 作为默认编码。很多同学初学 Java 时可能都遇到过一个正常编写 Java 类,在 windows 系统的命令控制台中运行却出现乱码的情况。

使用下面的命令可以输出 JDK 的当前编码。

# Mac 系统,默认 UTF-8
➜  ~ java -XshowSettings:properties -version 2>&1 | grep file.encoding
    file.encoding = UTF-8
    file.encoding.pkg = sun.io
➜  ~

下面编写一个简单的 Java 程序,输出默认字符编码,然后输出中文汉字 ”你好“,看看 Java 18 和 Java 17 运行区别。

系统环境:Windows 11

import java.nio.charset.Charset;

public class Hello
    public static void main(String[] args) 
        System.out.println(Charset.defaultCharset());        System.out.println("你好");    

从下面的运行结果中可以看到,使用 JDK 17 运行输出的默认字符编码是 GBK,输出的中文 ”你好“ 已经乱码了;乱码是因为 VsCode 默认的文本编辑器编码是 UTF-8,而中国地区的 Windows 11 默认字符编码是 GBK,也是 JDK 17 默认获取到的编码,所以会在控制台输出时乱码;而使用 JDK 18 输出的默认编码就是 UTF-8,所以可以正常的输出中文 ”你好“。

img

JEP 408:简单的 Web 服务器

在 Java 18 中,提供了一个新命令 jwebserver,运行这个命令可以启动一个简单的 、最小化的静态 Web 服务器,它不支持 CGI 和 Servlet,所以最好的使用场景是用来测试、教育、演示等需求。

其实在如 Python、Ruby、php、Erlang 等许多平台都提供了开箱即用的 Web 服务器,可见一个简单的 Web 服务器是一个常见的需求,Java 一直没有这方面的支持,现在可以了。

在 Java 18 中,使用 jwebserver 启动一个 Web 服务器,默认发布的是当前目录。

在当前目录创建一个网页文件 index.html

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>标题</h1>
</body>
</html>

启动 jwebserver.

➜  bin ./jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /Users/darcy/develop/jdk-18.jdk/Contents/Home/bin and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/

浏览器访问:

img

有请求时会在控制台输出请求信息:

127.0.0.1 - - [26/3月/2022:16:53:30 +0800] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [26/3月/2022:16:55:13 +0800] "GET / HTTP/1.1" 200 -

通过 help 参数可以查看 jwebserver 支持的参数。

➜  bin ./jwebserver --help
Usage: jwebserver [-b bind address] [-p port] [-d directory]
                  [-o none|info|verbose] [-h to show options]
                  [-version to show version information]
Options:
-b, --bind-address    - 绑定地址. Default: 127.0.0.1 (loopback).
                        For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory       - 指定目录. Default: current directory.
-o, --output          - Output format. none|info|verbose. Default: info.
-p, --port            - 绑定端口. Default: 8000.
-h, -?, --help        - Prints this help message and exits.
-version, --version   - Prints version information and exits.
To stop the server, press Ctrl + C.

JEP 413:Javadoc 中支持代码片段

在 Java 18 之前,已经支持在 Javadoc 中引入代码片段,这样可以在某些场景下更好的展示描述信息,但是之前的支持功能有限,比如我想高亮代码片段中的某一段代码是无能为力的。现在 Java 18 优化了这个问题,增加了 @snippet 来引入更高级的代码片段。

在 Java 18 之前,使用 <pre>@code ...</pre> 来引入代码片段。

/**
  * 时间工具类
  * Java 18 之前引入代码片段:
  * <pre>@code
  *     public static String timeStamp() 
  *        long time = System.currentTimeMillis();
  *         return String.valueOf(time / 1000);
  *     
  * </pre>
  *
  */

生成 Javadoc 之后,效果如下:

img

高亮代码片段

从 Java 18 开始,可以使用 @snippet 来生成注释,且可以高亮某个代码片段。

/**
 * 在 Java 18 之后可以使用新的方式
 * 下面的代码演示如何使用 @code Optional.isPresent:
 * @snippet :
 * if (v.isPresent()) 
 *     System.out.println("v: " + v.get());
 * 
 * 
 *
 * 高亮显示 println
 *
 * @snippet :
 * class HelloWorld 
 *     public static void main(String... args) 
 *         System.out.println("Hello World!");      // @highlight substring="println"
 *     
 * 
 * 
 *
 */

效果如下,更直观,效果更好。

img

正则高亮代码片段

甚至可以使用正则来高亮某一段中的某些关键词:

/**
  * 正则高亮:
  * @snippet :
  *   public static void main(String... args) 
  *       for (var arg : args)                  // @highlight region regex = "\\barg\\b"
  *           if (!arg.isBlank()) 
  *               System.out.println(arg);
  *           
  *                                             // @end
  *   
  *   
  */

生成的 Javadoc 效果如下:

img

替换代码片段

可以使用正则表达式来替换某一段代码。

/**
   * 正则替换:
   * @snippet :
   * class HelloWorld 
   *     public static void main(String... args) 
   *         System.out.println("Hello World!");  // @replace regex='".*"' replacement="..."
   *     
   * 
   * 
   */

这段注释会生成如下 Javadoc 效果。

class HelloWorld 
    public static void main(String... args) 
        System.out.println(...);
    

附:Javadoc 生成方式

# 使用 javadoc 命令生成 Javadoc 文档
➜  bin ./javadoc -public -sourcepath ./src -subpackages com -encoding utf-8 -charset utf-8 -d ./javadocout
# 使用 Java 18 的 jwebserver 把生成的 Javadoc 发布测试
➜  bin ./jwebserver -d /Users/darcy/develop/javadocout

访问测试:

img

JEP 416:使用方法句柄重新实现反射核心功能

Java 18 改进了 java.lang.reflect.MethodConstructor 的实现逻辑,使之性能更好,速度更快。这项改动不会改动相关 API ,这意味着开发中不需要改动反射相关代码,就可以体验到性能更好反射。

OpenJDK 官方给出了新老实现的反射性能基准测试结果。

Java 18 之前:

Benchmark                                     Mode  Cnt   Score  Error  Units
ReflectionSpeedBenchmark.constructorConst     avgt   10  68.049 ± 0.872  ns/opReflectionSpeedBenchmark.constructorPoly      avgt   10  94.132 ± 1.805  ns/op
ReflectionSpeedBenchmark.constructorVar       avgt   10  64.543 ± 0.799  ns/op
ReflectionSpeedBenchmark.instanceFieldConst   avgt   10  35.361 ± 0.492  ns/opReflectionSpeedBenchmark.instanceFieldPoly    avgt   10  67.089 ± 3.288  ns/op
ReflectionSpeedBenchmark.instanceFieldVar     avgt   10  35.745 ± 0.554  ns/op
ReflectionSpeedBenchmark.instanceMethodConst  avgt   10  77.925 ± 2.026  ns/op
ReflectionSpeedBenchmark.instanceMethodPoly   avgt   10  96.094 ± 2.269  ns/op
ReflectionSpeedBenchmark.instanceMethodVar    avgt   10  80.002 ± 4.267  ns/op
ReflectionSpeedBenchmark.staticFieldConst     avgt   10  33.442 ± 2.659  ns/op
ReflectionSpeedBenchmark.staticFieldPoly      avgt   10  51.918 ± 1.522  ns/op
ReflectionSpeedBenchmark.staticFieldVar       avgt   10  33.967 ± 0.451  ns/op
ReflectionSpeedBenchmark.staticMethodConst    avgt   10  75.380 ± 1.660  ns/op
ReflectionSpeedBenchmark.staticMethodPoly     avgt   10  93.553 ± 1.037  ns/op
ReflectionSpeedBenchmark.staticMethodVar      avgt   10  76.728 ± 1.614  ns/op

Java 18 的新实现:

Benchmark                                     Mode  Cnt    Score   Error  Units
ReflectionSpeedBenchmark.constructorConst     avgt   10   32.392 ± 0.473  ns/opReflectionSpeedBenchmark.constructorPoly      avgt   10  113.947 ± 1.205  ns/op
ReflectionSpeedBenchmark.constructorVar       avgt   10   76.885 ± 1.128  ns/op
ReflectionSpeedBenchmark.instanceFieldConst   avgt   10   18.569 ± 0.161  ns/opReflectionSpeedBenchmark.instanceFieldPoly    avgt   10   98.671 ± 2.015  ns/op
ReflectionSpeedBenchmark.instanceFieldVar     avgt   10   54.193 ± 3.510  ns/op
ReflectionSpeedBenchmark.instanceMethodConst  avgt   10   33.421 ± 0.406  ns/op
ReflectionSpeedBenchmark.instanceMethodPoly   avgt   10  109.129 ± 1.959  ns/op
ReflectionSpeedBenchmark.instanceMethodVar    avgt   10   90.420 ± 2.187  ns/op
ReflectionSpeedBenchmark.staticFieldConst     avgt   10   19.080 ± 0.179  ns/op
ReflectionSpeedBenchmark.staticFieldPoly      avgt   10   92.130 ± 2.729  ns/op
ReflectionSpeedBenchmark.staticFieldVar       avgt   10   53.899 ± 1.051  ns/op
ReflectionSpeedBenchmark.staticMethodConst    avgt   10   35.907 ± 0.456  ns/op
ReflectionSpeedBenchmark.staticMethodPoly     avgt   10  102.895 ± 1.604  ns/op
ReflectionSpeedBenchmark.staticMethodVar      avgt   10   82.123 ± 0.629  ns/op

可以看到在某些场景下性能稍微好些。

JEP 417:Vector API(三次孵化)

在 Java 16 中引入一个新的 API 来进行向量计算,它可以在运行时可靠的编译为支持的 CPU 架构,从而实现更优的计算能力。

在 Java 17 中改进了 Vector API 性能,增强了例如对字符的操作、字节向量与布尔数组之间的相互转换等功能。

现在在 JDK 18 中将继续优化其性能。

JEP 418:互联网地址解析 SPI

对于互联网地址解析 SPI,为主机地址和域名地址解析定义一个 SPI,以便java.net.InetAddress可以使用平台内置解析器以外的解析器。

InetAddress inetAddress = InetAddress.getByName("www.wdbyte.com");
System.out.println(inetAddress.getHostAddress());
// 输出
// 106.14.229.49

JEP 419:Foreign Function & Memory API (第二次孵化)

新的 API 允许 Java 开发者与 JVM 之外的代码和数据进行交互,通过调用外部函数,可以在不使用 JNI 的情况下调用本地库。

这是一个孵化功能;需要添加--add-modules jdk.incubator.foreign来编译和运行 Java 代码,Java 18 改进了相关 API ,使之更加简单易用。

历史

  • Java 14 JEP 370 (opens new window)[42]引入了外部内存访问 API(孵化器)。

  • Java 15 JEP 383 (opens new window)[43]引入了外部内存访问 API(第二孵化器)。

  • Java 16 JEP 389 (opens new window)[44]引入了外部链接器 API(孵化器)。

  • Java 16 JEP 393 (opens new window)[45]引入了外部内存访问 API(第三孵化器)。

  • Java 17 JEP 412 (opens new window)[46]引入了外部函数和内存 API(孵化器)。

JEP 420:switch 表达式(二次孵化)

从 Java 17 开始,对于 Switch 的改进就已经在进行了,Java 17 的 JEP 406 已经对 Switch 表达式进行了增强,使之可以减少代码量。

下面是几个例子:

// JDK 17 以前
static String formatter(Object o) 
    String formatted = "unknown";
    if (o instanceof Integer i) 
        formatted = String.format("int %d", i);
     else if (o instanceof Long l) 
        formatted = String.format("long %d", l);
     else if (o instanceof Double d) 
        formatted = String.format("double %f", d);
     else if (o instanceof String s) 
        formatted = String.format("String %s", s);
    
    return formatted;

而在 Java 17 之后,可以通过下面的写法进行改进:

// JDK 17 之后
static String formatterPatternSwitch(Object o) 
    return switch (o) 
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    ;

switch 可以和 null 进行结合判断:

static void testFooBar(String s) 
    switch (s) 
        case null         -> System.out.println("Oops");        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    

case 时可以加入复杂表达式:

static void testTriangle(Shape s) 
    switch (s) 
        case Triangle t && (t.calculateArea() > 100) ->            System.out.println("Large triangle");
        default ->
            System.out.println("A shape, possibly a small triangle");
    

case 时可以进行类型判断:

sealed interface S permits A, B, C 
final class A implements S 
final class B implements S 
record C(int i) implements S   // Implicitly final

static int testSealedExhaustive(S s) 
    return switch (s) 
        case A a -> 1;
        case B b -> 2;
        case C c -> 3;
    ;

扩展:JEP 406:Switch 的类型匹配(预览)[47]

JEP 421:弃用删除相关

在未来将删除 Finalization,目前 Finalization 仍默认保持启用状态,但是已经可以手动禁用;在未来的版本中,将会默认禁用;在以后的版本中,它将被删除。需要进行资源管理可以尝试 try-with-resources 或者 java.lang.ref.Cleaner

参考资料

[1]

恢复始终严格的浮点语义: https://openjdk.java.net/jeps/306

[2]

增强的伪随机数生成器: https://openjdk.java.net/jeps/356

[3]

使用新的 macOS 渲染库: https://openjdk.java.net/jeps/382

[4]

支持 macOS/AArch64 架构: https://openjdk.java.net/jeps/391

[5]

删除已启用的 Applet API: https://openjdk.java.net/jeps/398

[6]

更强的封装 JDK 内部封装: https://openjdk.java.net/jeps/403

[7]

Switch 模式匹配(预览): https://openjdk.java.net/jeps/406

[8]

移除 RMI Activation: https://openjdk.java.net/jeps/407

[9]

密封类(Sealed Classes): https://openjdk.java.net/jeps/409

[10]

JEP 401:移除实验性的 AOT 和 JIT 编译器: https://openjdk.java.net/jeps/410

[11]

弃用 Security Manager: https://openjdk.java.net/jeps/411

[12]

外部函数和内存 API(孵化器): https://openjdk.java.net/jeps/412

[13]

Vector API(第二孵化器): https://openjdk.java.net/jeps/414

[14]

指定上下文的反序列化过滤器: https://openjdk.java.net/jeps/415

[15]

增强的伪随机数生成器: https://openjdk.java.net/jeps/356

[16]

macOS Mojave 10.14 Release Notes: https://developer.apple.com/documentation/macos-release-notes/macos-mojave-10_14-release-notes?preferredLanguage=occ#3035786

[17]

Apple Metal: https://developer.apple.com/metal/

[18]

Java 16 新功能介绍 - JEP 386: https://www.wdbyte.com/java/java-16/

[19]

关键内部 API: https://openjdk.java.net/jeps/260#Description

[20]

JEP 403:更强的 JDK 内部封装: https://openjdk.java.net/jeps/403

[21]

Java 16 新功能介绍: https://www.wdbyte.com/java/java-16/

[22]

JEP 406:switch 的类型匹配(预览): https://openjdk.java.net/jeps/406

[23]

JEP 407:移除 RMI Activation: https://openjdk.java.net/jeps/407

[24]

Java 16 新功能介绍: https://www.wdbyte.com/java/java-16/

[25]

JEP 409: Sealed Classes: https://openjdk.java.net/jeps/409

[26]

JEP 370: https://openjdk.java.net/jeps/370

[27]

JEP 383: https://openjdk.java.net/jeps/383

[28]

JEP 389: https://openjdk.java.net/jeps/389

[29]

JEP 393: https://openjdk.java.net/jeps/393

[30]

JEP 412: https://openjdk.java.net/jeps/412

[31]

JEP 412:外部函数和内存 API (孵化): https://openjdk.java.net/jeps/412

[32]

JEP 415:指定上下文的反序列化过滤器: https://openjdk.java.net/jeps/415

[33]

默认为 UTF-8: https://openjdk.java.net/jeps/400

[34]

简单的网络服务器: https://openjdk.java.net/jeps/408

[35]

Java API 文档中的代码片段: https://openjdk.java.net/jeps/413

[36]

使用方法句柄重新实现核心反射: https://openjdk.java.net/jeps/416

[37]

Vector API(三次孵化): https://openjdk.java.net/jeps/417

[38]

互联网地址解析 SPI: https://openjdk.java.net/jeps/418

[39]

Foreign Function & Memory API (二次孵化): https://openjdk.java.net/jeps/419

[40]

switch 模式匹配(二次预览): https://openjdk.java.net/jeps/420

[41]

弃用完成删除: https://openjdk.java.net/jeps/421

[42]

JEP 370 (opens new window): https://openjdk.java.net/jeps/370

[43]

JEP 383 (opens new window): https://openjdk.java.net/jeps/383

[44]

JEP 389 (opens new window): https://openjdk.java.net/jeps/389

[45]

JEP 393 (opens new window): https://openjdk.java.net/jeps/393

[46]

JEP 412 (opens new window): https://openjdk.java.net/jeps/412

[47]

JEP 406:Switch 的类型匹配(预览): https://www.wdbyte.com/java/java-17/#_7-jep-406-switch-的类型匹配-预览

我的新书《深入理解Java核心技术》已经上市了,上市后一直蝉联京东畅销榜中,目前正在6折优惠中,想要入手的朋友千万不要错过哦~长按二维码即可购买~

长按扫码享受6折优惠


往期推荐

绝了!这个MySQL故障定位方法太好用了


再有人问你什么是分库分表,直接把这篇文章发给他


JB出品,下一代IDE!!


有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

好文章,我在看❤️

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于换掉 Java 8 !Java 17&18 新特性真香的主要内容,如果未能解决你的问题,请参考以下文章

Java 18 要来了,你不会还在用 Java 8 吧?

怎么用java打印图形三: 1 7 12 16 19 21 2 8 13 17 20 3 9 14 18 4 10 15 5 11 6

java问题。输出结果是0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 左对齐!最好用多维数组。

Java 18 都要来了,你不会还在用Java 8吧?

Java 18 要来了,你不会还在用Java 8吧?

Java 18 都要来了,你不会还在用 Java 8 吧?