Java 19 新功能介绍

Java 19 在2022 年 9 月 20 日正式发布,Java 19 不是一个长期支持版本,直到 2023 年 3 月它将被 JDK 20 取代,这次更新共带来 7 个新功能。

➜  bin ./java -version

openjdk version "19" 2022-09-20

OpenJDK Runtime Environment (build 19+36-2238)

OpenJDK 64-Bit Server VM (build 19+36-2238, mixed mode, sharing)

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

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

Java 19 带来的 7 个新特性:

1.png

Java 19 新功能介绍是 Java 新特性系列文章中的一部分。

系列详情可以浏览:https://www.wdbyte.com/java-feature/

JEP 405: Record 模式匹配(预览)

record 是一种全新的类型,它本质上是一个 final 类,同时所有的属性都是 final 修饰,它会自动编译出 public get hashcode 、equals、toString 等方法,减少了代码编写量。Record 在 Java 14 中被提出,在 Java 15 中二次预览,在 Java 16 中正式发布。

示例:编写一个 Dog record 类,定义 name 和 age 属性。

package com.wdbyte;

public record Dog(String name, Integer age) {

}

Record 的使用。

package com.wdbyte;

public class Java14Record {

    public static void main(String[] args) {

        Dog dog1 = new Dog("牧羊犬", 1);

        Dog dog2 = new Dog("田园犬", 2);

        Dog dog3 = new Dog("哈士奇", 3);

        System.out.println(dog1);

        System.out.println(dog2);

        System.out.println(dog3);

    }

}

输出结果:

Dog[name=牧羊犬, age=1]

Dog[name=田园犬, age=2]

Dog[name=哈士奇, age=3]

在 Java 19 中,为 Record 带来了增强的模式匹配,在使用 instanceof 后,可以进行类型转换。

public class RecordTest {

    public static void main(String[] args) {

        Object dog1 = new Dog("牧羊犬", 1);

        if(dog1 instanceof Dog dogTemp){

            System.out.println(dogTemp.name());

         }

    }

}

record Dog( String name, Integer age ){

}

// ➜  bin ./java  RecordTest.java

// 牧羊犬

甚至可以在使用 instanceof 时直接得到 Record 中的变量引用。

public class RecordTest2 {

    public static void main(String[] args) {

        Object dog1 = new Dog("牧羊犬", 1);

        if(dog1 instanceof Dog(String name,Integer age)){

            System.out.println(name+":"+age);

         }

    }

}

record Dog( String name, Integer age ){

}

//➜  bin ./java –enable-preview –source 19 RecordTest2.java

//注: RecordTest2.java 使用 Java SE 19 的预览功能。

//注: 有关详细信息,请使用 -Xlint:preview 重新编译。

//牧羊犬:1

扩展:

Java 14 instanceof 类型推断

Java 16 Record 介绍

JEP425:虚拟化线程(预览)

这是一个非常实用的新特性。虚拟线程从Java19开始逐渐引入。虚拟线程是一个轻量级的线程,可以显著减少代码编写,提高可维护性,增加系统的吞吐量。

引入的原因

Thread一直是Java并发编程中非常重要的一部分,Thread是Java中的并发单元,每一个Thread线程都提供了存储局部变量和方法调用的堆栈,以及线程上下文等相关信息。

但问题是,线程和过程是一样的,是一种昂贵的资源。JDK将Thread线程实现为操作系统线程的包装器,这意味着成本高,数量有限。因此,我们将使用线程池来管理线程,同时限制线程的数量。例如,常用的Tomcat会对每个请求单独使用一个线程进行请求处理,同时限制要求的线程数量,防止线程过多而崩溃;这很有可能在CPU或网络连接耗尽之前,线程数量已经耗尽,从而限制了web服务的吞吐量。

看到这里,你可能会说,你可以放弃请求和线程一一对应的方式,用异步编程来解决这个问题,把请求分段处理,组合成顺序管,通过一套API来管理,这样你就可以用有限的线程来处理超过线程数量的请求。当然,这是可以的,但随之而来的问题是:

学习异步编程需要额外的学习。

随着代码复杂度的增加,语言的基本顺序组合运算被放弃。

堆放上下文的信息变得难以跟踪。

Debug很难。

与Java平台本身的编程风格相冲突,Java并发单元为Thread,此时为异步管道。

虚拟线程

由于上述原因,Java19引入了虚拟线程,在使用体验上与Thread没有什么不同,与之前的API相兼容,但相比之下,虚拟线程资源占用很少,同时优化了硬件的使用效率,因此非常容易使用,不需要被池化。

以下是一个例子,创建10万个线程,然后休眠1秒钟,最后打印需要时间。如果是传统的Thread线程,资源非常紧张;如果是线程池,一定有一些线程在等待线程释放;但是你可以通过虚拟线程瞬间完成。

import java.util.concurrent.Executors;

import java.util.stream.IntStream;

public class ThreadTest {

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {

            IntStream.range(0, 100_000).forEach(i -> {

                executor.submit(() -> {

                    Thread.sleep(1000);

                    return i;

                });

            });

        } // executor.close() 会被自动调用

        // 提交了 10 万个虚拟线程,每个线程休眠 1 秒钟,1秒左右完成

        System.out.println("耗时:" + (System.currentTimeMillis() – start)+"ms");

    }

}

执行后发现 1.3 秒执行完毕,速度惊人。

➜  bin ./java –enable-preview –source 19  ThreadTest.java

注: ThreadTest.java 使用 Java SE 19 的预览功能。

注: 有关详细信息,请使用 -Xlint:preview 重新编译。

耗时:1309ms

➜  bin

注意:虚拟线程只是增加程序的吞吐量,并不能提高程序的处理速度。

JEP 427: switch 模式匹配 (三次预览)

Switch 模式匹配在 Java 17 中已经引入,在 Java 18 中二次预览,现在在 Java 19 中进行三次预览,功能和在 Java 18 新功能介绍 – Switch 中介绍的一样,改进后的 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 的类型匹配(预览)

JEP 422: Linux/RISC-V Port

RISC-V是一个免费和开源的 RISC 指令集架构 (ISA),实际上 RISC-V 是一系列相关的 ISA,现在 Java 19 开始对其进行支持。

JEP 424: 外部函数 & 内存 API (预览)

此功能引入的 API 允许 Java 开发者与 JVM 之外的代码和数据进行交互,通过调用外部函数(JVM之外)和安全的访问外部内存(非 JVM 管理),让 Java 程序可以调用本机库并处理本机数据,而不会像 JNI 一样存在很多安全风险。

这不是一个新功能,自 Java 14 就已经引入,此次对其进行了性能、通用性、安全性、易用性上的优化。

历史

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

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

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

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

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

Java 18 JEP 419引入了外部函数和内存 API(二次孵化器)。

其他更新

JEP 426: Vector API (四次孵化)

通过将在运行时可靠地编译为支持的 CPU 架构上的向量指令的向量计算表示,与等效的标量计算相比,实现了卓越的性能。此功能已经第四次孵化,在之前 Java 16 ~ Java 18 中都有介绍,这里不做赘述。

JEP 428: Structured Concurrency (孵化)

通过简化多线程编程并将在不同线程中运行的多个任务视为单个工作单元,简化错误处理和取消,提高可靠性并增强可观察性。

一如既往,文章中代码存放在 Github.com/niumoo/javaNotes.

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注