前提

JDK192022-09-20发布GA版本,本文将会详细介绍JDK19新特性的使用。

新特性列表

新特性列表如下:

  • JPE-405Record模式(预览功能)
  • JPE-422JDK移植到Linux/RISC-V
  • JPE-424:外部函数和内存API(预览功能)
  • JPE-425:虚拟线程,也就是协程(预览功能)
  • JPE-426:向量API(第四次孵化)
  • JPE-427switch匹配模式(第三次预览)
  • JPE-428:结构化并发(孵化功能)

新特性使用详解

下面就每个新特性介绍其使用方式。

Record模式

使用Record模式增强Java编程语言以解构Record值。可以嵌套Record模式和Type模式,以实现强大的、声明性的和可组合的数据导航和处理形式。这个描述看起来有点抽象,下面举几个JEP-405的例子结合文字理解一下。以JDK16扩展的instanceof关键字下使用Type模式来看:

  1. // JDK16以前
  2. private static void oldInstanceOf(Object x) {
  3. if (x instanceof String) {
  4. String s = (String) x;
  5. System.out.println(s);
  6. }
  7. }
  8. // JDK16或之后启用instanceof下的Type模式
  9. private static void newInstanceOfTypePattern(Object x) {
  10. if (x instanceof String s) {
  11. System.out.println(s);
  12. }
  13. }

Type模式在JDK17JDK18扩展到switch预览功能中,应用于其case标签:

  1. // DEMO-1
  2. private static void switchTypePattern(String s) {
  3. switch (s) {
  4. case null -> System.out.println("NULL");
  5. case "Foo", "Bar" -> System.out.println("Foo or Bar");
  6. default -> System.out.println("Default");
  7. }
  8. }
  9. // DEMO-2
  10. interface Shape{}
  11. class Rectangle implements Shape{}
  12. class Triangle implements Shape{
  13. public int calculateArea(){
  14. return 200;
  15. }
  16. }
  17. private static void switchTypePatternForShape(Shape shape) {
  18. switch (shape) {
  19. case null:
  20. break;
  21. case Rectangle r:
  22. System.out.printf("Rectangle[%s]\n", r);
  23. break;
  24. case Triangle t:
  25. if (t.calculateArea() > 100) {
  26. System.out.printf("Large triangle[%s]\n", t);
  27. }
  28. default:
  29. System.out.println("Default shape");
  30. }
  31. }
  32. // DEMO-3 patterns in labels
  33. private static void switchTypeForLabels(Object x) {
  34. String formatted = switch (x) {
  35. case Integer i -> String.format("int => %d", i);
  36. case Long l -> String.format("long => %d", l);
  37. case Double d -> String.format("double => %f", d);
  38. case String s -> String.format("string => %s", s);
  39. default -> x.toString();
  40. };
  41. }

本次的Record模式预览功能就是基于record关键字实现上面的Type类型或者switch模式。例如:

  1. // DEMO-1
  2. record Point(int x,int y){}
  3. private static void printSum(Object o){
  4. if (o instanceof Point(int x,int y)){
  5. System.out.println(x + y);
  6. }
  7. }

record类中如果存在泛型参数可以进行类型转换和推导,例如:

  1. // DEMO-2
  2. record Holder<T>(T target){}
  3. // 擦除后
  4. private void convert(Holder<Object> holder){
  5. if (Objects.nonNull(holder) && holder instanceof Holder<Object>(String target)) {
  6. System.out.printf("string => %s\n", target);
  7. }
  8. }
  9. // 非擦除
  10. private <T> void convert(Holder<T> holder){
  11. if (Objects.nonNull(holder) && holder instanceof Holder<T>(String target)) {
  12. System.out.printf("string => %s\n", target);
  13. }
  14. }

然后看recordswitch结合使用:

  1. // DEMO-3
  2. sealed interface I permits C, D {}
  3. final class C implements I {}
  4. final class D implements I {}
  5. Second<I,I> second;
  6. private void recordSwitch() {
  7. second = new Second<>(new D(), new C());
  8. // second = new Second<>(new C(), new D());
  9. switch (second) {
  10. case Second<I, I>(C c,D d) -> System.out.printf("c => %s,d => %s", c, d);
  11. case Second<I, I>(D d,C c) -> System.out.printf("d => %s,c => %s", d, c);
  12. default -> System.out.println("default");
  13. }
  14. }

这种模式比较复杂,因为涉及到record类、switch模式、泛型参数并且参数类型是接口,case子句处理的时候必须覆盖该泛型参数接口的所有子类型

不得不说,JDK引入的语法糖越来越复杂,功能看起来是强大的,但是编码的可读性在未适应期有所下降

Linux/RISC-V移植

通过Linux/RISC-V移植,Java将获得对硬件指令集的支持,该指令集已被广泛的语言工具链支持。RISC-V是一种包含矢量指令的通用64ISA,目前该端口支持以下的HotSpot VM选项:

  • 模板解释器
  • 客户端JIT编译器
  • 服务端JIT编译器
  • 包括ZGCShenandoah在内的主流垃圾收集器

该移植基本已经完成,JEP的重点是将该端口集成到JDK的主仓库中。

外部函数和内存API

外部函数和内存API的主要功能是引入一组APIJava程序可以通过该组APIJava运行时之外的代码和数据进行交互。有以下目标:

  • 易用性:通过卓越的纯Java开发模型代替JNI
  • 高性能:提供能与当前JNI或者Unsafe相当甚至更优的性能
  • 通用性:提供支持不同种类的外部内存(如本地内存、持久化内存和托管堆内存)的API,并随着时间推移支持其他操作系统甚至其他语言编写的外部函数
  • 安全性:允许程序对外部内存执行不安全的操作,但默认警告用户此类操作

核心的API和功能如下:

  • 分配外部内存:MemorySegmentMemoryAddressSegmentAllocator
  • 操作和访问结构化的外部内存:MemoryLayoutVarHandle
  • 控制外部内存:MemorySession
  • 调用外部函数:LinkerFunctionDescriptorSymbolLookup

这些API统称为FFM API,位于java.base模块的java.lang.foreign包中。由于API比较多并且不算简单,这里只举一个简单的例子:

  1. public class AllocMemoryMain {
  2. public static void main(String[] args) {
  3. new AllocMemoryMain().allocMemory();
  4. }
  5. /**
  6. * 分配内存
  7. * struct Point {
  8. * int x;
  9. * int y;
  10. * } pts[10];
  11. */
  12. public void allocMemory() {
  13. Random random = new Random();
  14. // 分配本地内存
  15. MemorySegment segment = MemorySegment.allocateNative(2 * 4 * 10, MemorySession.openImplicit());
  16. // 创建顺序内存布局
  17. SequenceLayout ptsLayout = MemoryLayout.sequenceLayout(10, MemoryLayout.structLayout(
  18. ValueLayout.JAVA_INT.withName("x"),
  19. ValueLayout.JAVA_INT.withName("y")));
  20. // 对内存设置值
  21. VarHandle xHandle = ptsLayout.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("x"));
  22. VarHandle yHandle = ptsLayout.varHandle(MemoryLayout.PathElement.sequenceElement(), MemoryLayout.PathElement.groupElement("y"));
  23. for (int i = 0; i < ptsLayout.elementCount(); i++) {
  24. int x = i * random.nextInt(100);
  25. int y = i * random.nextInt(100);
  26. xHandle.set(segment,/* index */ (long) i,/* value to write */x); // x
  27. yHandle.set(segment,/* index */ (long) i,/* value to write */ y); // y
  28. System.out.printf("index => %d, x = %d, y = %d\n", i, x, y);
  29. }
  30. // 获取内存值
  31. int xValue = (int) xHandle.get(segment, 5);
  32. System.out.println("Point[5].x = " + xValue);
  33. int yValue = (int) yHandle.get(segment, 6);
  34. System.out.println("Point[6].y = " + yValue);
  35. }
  36. }
  37. // 某次执行输出结果
  38. index => 0, x = 0, y = 0
  39. index => 1, x = 79, y = 16
  40. index => 2, x = 164, y = 134
  41. index => 3, x = 150, y = 60
  42. index => 4, x = 152, y = 232
  43. index => 5, x = 495, y = 240
  44. index => 6, x = 54, y = 162
  45. index => 7, x = 406, y = 644
  46. index => 8, x = 464, y = 144
  47. index => 9, x = 153, y = 342
  48. Point[5].x = 495
  49. Point[6].y = 162

FFM API是一组极度强大的API,有了它可以灵活地安全地使用外部内存和外部(跨语言)函数。

虚拟线程

虚拟线程,就是轻量级线程,也就是俗称的协程,虚拟线程的资源分配和调度由VM实现,与平台线程(platform thread)有很大的不同。从目前的源代码来看,虚拟线程的状态管理、任务提交、休眠和唤醒等也是完全由VM实现。可以通过下面的方式创建虚拟线程:

  1. // 方式一:直接启动虚拟线程,因为默认参数原因这样启动的虚拟线程名称为空字符串
  2. Thread.startVirtualThread(() -> {
  3. Thread thread = Thread.currentThread();
  4. System.out.printf("线程名称:%s,是否虚拟线程:%s\n", thread.getName(), thread.isVirtual());
  5. });
  6. // 方式二:Builder模式构建
  7. Thread vt = Thread.ofVirtual().allowSetThreadLocals(false)
  8. .name("VirtualWorker-", 0)
  9. .inheritInheritableThreadLocals(false)
  10. .unstarted(() -> {
  11. Thread thread = Thread.currentThread();
  12. System.out.printf("线程名称:%s,是否虚拟线程:%s\n", thread.getName(), thread.isVirtual());
  13. });
  14. vt.start();
  15. // 方式三:Factory模式构建
  16. ThreadFactory factory = Thread.ofVirtual().allowSetThreadLocals(false)
  17. .name("VirtualFactoryWorker-", 0)
  18. .inheritInheritableThreadLocals(false)
  19. .factory();
  20. Thread virtualWorker = factory.newThread(() -> {
  21. Thread thread = Thread.currentThread();
  22. System.out.printf("线程名称:%s,是否虚拟线程:%s\n", thread.getName(), thread.isVirtual());
  23. });
  24. virtualWorker.start();
  25. // 可以构建"虚拟线程池"
  26. ExecutorService executorService = Executors.newThreadPerTaskExecutor(factory);

由于虚拟线程的功能还处于预览阶段,创建协程的时候无法自定义执行器(准确来说是运载线程),目前所有虚拟线程都是交由一个内置的全局ForkJoinPool实例执行,实现方式上和JDK8中新增的并行流比较接近。另外,目前来看虚拟线程和原来的JUC类库是亲和的,可以把虚拟线程替换原来JUC类库中的Thread实例来尝试使用(在生产应用建议等该功能正式发布)

向量API

向量API目前是第四次孵化,功能是表达向量计算,在运行时编译为CPU 架构上的最佳向量指令,从而实现优于等效标量计算的性能。目前相关API都在jdk.incubator.vector包下,使用的例子如下:

  1. static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;
  2. private static void vectorComputation(float[] a, float[] b, float[] c) {
  3. for (int i = 0; i < a.length; i += SPECIES.length()) {
  4. var m = SPECIES.indexInRange(i, a.length);
  5. var va = FloatVector.fromArray(SPECIES, a, i, m);
  6. var vb = FloatVector.fromArray(SPECIES, b, i, m);
  7. var vc = va.mul(va).add(vb.mul(vb)).neg();
  8. vc.intoArray(c, i, m);
  9. }
  10. }
  11. public static void main(String[] args) {
  12. float[] a = new float[]{1.0f, 3.0f, 2.0f};
  13. float[] b = {1.0f, -1.0f, 5.0f};
  14. float[] c = {1.0f, 6.0f, 1.0f};
  15. vectorComputation(a, b, c);
  16. System.out.println(Arrays.toString(c));
  17. }

Vector有很多特化子类,可以通过不同的VectorSpecies进行定义。

switch匹配模式

switch匹配模式第三次预览,主要是对匹配模式进行了扩展。主要有几点改进:

  • 增强类型校验,case子句支持多种类型
  1. record Point(int i, int j) {}
  2. enum Color { RED, GREEN, BLUE; }
  3. private void multiTypeCase(Object o) {
  4. switch (o) {
  5. case null -> System.out.println("null");
  6. case String s -> System.out.println("String");
  7. case Color c -> System.out.println("Color: " + c.toString());
  8. case Point p -> System.out.println("Record class: " + p.toString());
  9. case int[] ia -> System.out.println("Array of ints of length" + ia.length);
  10. default -> System.out.println("Something else");
  11. }
  12. }
  • 增强表达式和语句的表现力和适用性,可以实现selector模式
  1. private int selector(Object o) {
  2. return switch (o) {
  3. case String s -> s.length();
  4. case Integer i -> i;
  5. default -> 0;
  6. };
  7. }
  • 扩展模式变量声明范围
  1. private void switchScope(Object o) {
  2. switch (o) {
  3. case Character c
  4. when c.charValue() == 7:
  5. System.out.println("Seven!");
  6. break;
  7. default:
  8. break;
  9. }
  10. }
  • 优化null处理
  1. private void switchNull(Object o) {
  2. switch (o) {
  3. case null -> System.out.println("null!");
  4. case String s -> System.out.println("String");
  5. default -> System.out.println("Something else");
  6. }
  7. }

结构化并发

结构化并发功能在孵化阶段,该功能旨在通过结构化并发库来简化多线程编程。结构化并发提供的特性将在不同线程中运行的多个任务视为一个工作单元,以简化错误处理和取消,提高了可靠性和可观测性。

  1. record User(String name, Long id){}
  2. record Order(String orderNo, Long id){}
  3. record Response(User user, Order order){}
  4. private User findUser(){
  5. throw new UnsupportedOperationException("findUser");
  6. }
  7. private Order fetchOrder(){
  8. throw new UnsupportedOperationException("fetchOrder");
  9. }
  10. private Response handle() throws ExecutionException, InterruptedException {
  11. try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
  12. Future<User> user = scope.fork(() -> findUser());
  13. Future<Order> order = scope.fork(() -> fetchOrder());
  14. scope.join(); // Join both forks
  15. scope.throwIfFailed(); // ... and propagate errors
  16. // Here, both forks have succeeded, so compose their results
  17. return new Response(user.resultNow(), order.resultNow());
  18. }
  19. }

参考资料

  • JDK 19https://openjdk.org/projects/jdk/19,文中直接应用部分文档描述的翻译

(本文完,c-2-d e-a-20220923)

JDK19新特性使用详解的更多相关文章

  1. HTML5新特性及详解

    什么是HTML5:HTML5 是下一代的HTML,将成为 HTML.XHTML 以及 HTML DOM 的新标准. 为 HTML5 建立的一些规则: 新特性应该基于 HTML.CSS.DOM 以及 J ...

  2. iOS7新特性-NSURLSession详解

    前言:本文由DevDiv版主@jas 原创翻译,转载请注明出处!原文:http://www.shinobicontrols.com/b ... day-1-nsurlsession/ 大家都知道,过去 ...

  3. Php5.5新特性 Generators详解

    在PHP5.5.0版本中,新增了生成器(Generators)特性,用于简化实现迭代器接口(Iterator)创建简单的迭代器的复杂性. 通过生成器,我们可以轻松的使用foreach迭代一系列的数据, ...

  4. Java8新特性--lamada详解

    最近玩了一下这个,感觉挺有趣的,语法使用起来很简洁,让代码看起来挺清爽易读的. 看了一下源码,发现挺充分的利用了jak1.5的特性(注解.泛型). 但是,具体的实现流程还是有点不通透,先Mark,等用 ...

  5. Servlet3.0新特性使用详解

    可插拔的Web框架 几乎所有基于Java的web框架都建立在servlet之上.现今大多数web框架要么通过servlet.要么通过Web.xml插入.利用标注(Annotation)来定义servl ...

  6. Java8新特性: CompletableFuture详解

    CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调.流式处理.多个Future组合处理的能力,使Java在处理多任务的 ...

  7. ios新特征 ARC详解

    IOS ARC 分类: IOS ARC2013-01-17 09:16 2069人阅读 评论(0) 收藏 举报   目录(?)[+]   关闭工程的ARC(Automatic Reference Co ...

  8. ios中键值编码kvc和键值监听kvo的特性及详解

    总结: kvc键值编码  1.就是在oc中可以对属性进行动态读写(以往都是自己赋值属性)           2. 如果方法属性的关键字和需要数据中的关键字相同的话                  ...

  9. [转]Oracle 11g R2 RAC高可用连接特性 – SCAN详解

    原文地址:http://czmmiao.iteye.com/blog/2124373   昨天帮朋友解决11g RAC SCAN问题,当时为这朋友简单解答了一些SCAN特性相关的问题,但我知道这仅仅是 ...

随机推荐

  1. 零基础学Java(7)大数

    大数 如果基本的整数和浮点数精度不能够满足需求,那么可以使用java.math包中两个很有用的类:BigInteger和BigDecimal.这两个类可以处理包含任意长度数字序列的数值.BigInte ...

  2. 字符串运算符&&三元运算符

    public class Demo01 { public static void main(String[] args) { //字符串连接符 + String int a=20; int b=10; ...

  3. 第四天python3 python解析式-生成器-迭代器

    标准库datetime datetime模块  对日期.时间.时间戳的处理 datetime类 类方法: today() 返回本地时区当前时间的datetime对象: now(tz=None) 返回当 ...

  4. python 日志类

    简介 在所有项目中必不可少的一定是日志记录系统,python为我们提供了一个比较方便的日志模块logging,通常,我们都会基于此模块编写一个日志记录类,方便将项目中的日志记录到文件中. loggin ...

  5. LabVIEW图形化的AI视觉开发平台(非NI Vision),大幅降低人工智能开发门槛

    前言 之前每次进行机器学习和模型训练的时候发现想要训练不同模型的时候需要使用不同的框架,有时候费了九牛二虎之力终于写下了几百行代码之后,才发现环境调试不通,运行效率也差强人意,于是自己写了一个基于La ...

  6. STC8H开发(十五): GPIO驱动Ci24R1无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  7. java-Collection,List简单使用与方法/(集合使用-中)

    1.1集合只存放引用类型的元素并且集合存放的时元素的引用(地址)1.2新循环遍历集合 Collection c = new ArrayList(); c.add("one"); c ...

  8. [WPF]使用DispatcherUnhandledException捕捉未经处理的异常

    使用DispatcherUnhandledException捕捉未经处理的异常 using System.Windows; namespace Test02 { /// <summary> ...

  9. Spring源码-入门

    一.测试类 public class Main { public static void main(String[] args) { ApplicationContext applicationCon ...

  10. java数组---初始化

    public class ArrayDemo { public static void main(String[] args) { int[] a={1,2,3,4,5,6,7,8,9}; //静态初 ...