10万字总结Java8到21新特性详解

文章目录

Java 17 新特性—概述

Java 17,发布于 2021 年 9 月,是一个长期支持(LTS)版本。

img

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

在 Java 17 之前,Java 的伪随机数生成主要依赖于 java.util.Random 类及其子类,如 ThreadLocalRandomSecureRandom。这些类虽然功能强大,但在某些特定应用场景中存在局限性,如需要特定类型的随机数生成器(例如具有更长周期的生成器),或需要更细粒度的控制和更广泛的算法选择。

Java 17 引入增强的伪随机数生成器其目的是在 Java 标准库中引入更多种类的随机数生成器,并提供一种更为统一且易于使用的方式来访问和使用这些生成器。它的主要内容包括:

  1. 新的接口和实现:引入新的接口 RandomGenerator,以及多个实现此接口的类,每个类代表一种不同类型的随机数生成器。
  2. 更广泛的算法选择:提供了多种基于不同算法的随机数生成器,比如 LCG(线性同余生成器)、Xoroshiro、Xoshiro 等,每种算法都有其特点和适用场景。
  3. 更好的性能和质量:新的生成器在性能和生成数质量方面进行了优化,以满足更加复杂和高效的应用需求。

JEP 382:新的 macOS 渲染管线

在 Java 17 之前,Java 在 macOS 平台上的图形渲染主要依赖于 OpenGL。随着 Apple 宣布逐步废弃 OpenGL 支持,并推荐使用更现代的 Metal API,Java 需要适应这一变化,以确保其在 macOS 平台上的图形性能和兼容性。

该特性是引入一个新的渲染管道,使用 Apple 的 Metal API 替代 OpenGL。目前默认情况下,这是禁用的,因此渲染仍然使用OpenGL API;要启用metal,应用程序应通过设置系统属性指定其使用:

-Dsun.java2d.metal=true
复制代码

JEP 391:macOS/AArch64 端口

由于早期的 Java 版本并不支持在 ARM 架构的 M1 上运行,这就需要 Java 在 macOS 上支持 ARM64 架构(即 AArch64),从而确保 Java 应用程序和开发者能够在这个新平台上继续运行和开发。

Java 17 引入该特性,去主要目标是在 macOS 上为 ARM64 架构(AArch64)提供官方支持,解决以下问题:

  1. 兼容性:确保 Java 可以在 Apple 的 M1 芯片上顺利运行。
  2. 性能:优化 Java 在 ARM64 架构上的性能,使其充分利用 M1 芯片的高效能。

JEP 398:移除 Applet API

Applet API 是 Java 最早期的组成部分之一,它允许在浏览器中运行 Java 程序。随着时间的推移,网络技术发展,特别是 HTML5 和 JavaScript 的崛起,使得 Applet 的重要性大大降低。同时,Applets 通常需要浏览器插件来运行,这导致了安全性问题和兼容性问题。且随着主流浏览器逐渐放弃对 Java 插件的支持,Applet 的实用性进一步下降。

Java 17 将Applet API 标记为弃用,并在未来的 Java 版本中移除它。

JEP 406:模式匹配的 Swith 表达式(预览)

在 Java 16 中, JEP 394 扩展了 instanceof 运算符,可以采用类型模式并执行模式匹配。虽然可以不需要强制转换了,但是仍然需要大量的 if...else。而 Switch 表达式虽然简化了 if…else ,但是它无法像 instanceof 一样不需要强制转换。为了解决这个痛点,Java 17 引入模式匹配的 Switch 表达式特性 ,目前该特性为预览特性。

该特性扩展了 switch 表达式和语句,允许它们使用模式匹配,这就意味着我们可以在 switch 的 case 标签中使用模式,如类型模式,使得代码更加灵活和表达性更强。而且也无需进行显式的类型转换了。例如,可以使用 case Integer i 这样的语法来匹配并自动转换类型。

JEP 407:删除 RMI 激活

RMI (远程方法调用) 激活系统是 Java RMI 框架的一部分,它允许远程激活对象。它在 Java 15 中被标记为弃用,Java 17 删除 RMI 激活。

JEP 409:密封类(正式特性)

为了限制 Java 的继承,Java 15 引入密封类作为预览特性。密封类允许类设计者明确控制哪些其他类或接口可以实现或扩展它们,从而提供更精确的多态性控制。同时,Java 16 第二次作为预览特性,最终在 Java 17 中成为正式特性。

Java 版本 更新类型 JEP 更新内容
Java 15 预览特性 JEP 360 引入了密封类作为预览特性。
Java 16 第二次预览 JEP 397
Java 17 正式特性 JEP 409 成为正式特性。

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

Java 9 引入了实验性的 Ahead-of-Time (AOT) 编译器和一个实验性的 Just-In-Time (JIT) 编译器,即 Graal 编译器。这些特性最初被引入是为了探索和评估在 Java 虚拟机中提供不同类型编译器的可能性。经过一段时间的实验和社区反馈,发现这些实验性编译器的维护成本较高,而且在实际应用中的使用率并不高。

故而,Java 17 将 Jaotc AOT 编译器和基于 Graal 编译器的实验性 JIT 编译器移除。这样可以简化 JDK 的代码库,降低维护成本。

JEP 411:废弃安全管理器

安全管理器是 Java 平台的一部分,用于提供对 Java 应用的安全限制。它在早期 Java 版本中是重要的安全特性,用于控制 Applets 和其他类型的应用的权限。但是,随着 Java 平台的发展和安全模型的演进,安全管理器的重要性逐渐降低,现代应用通常通过操作系统级别的安全措施和应用架构设计来实现安全。

Java 17 将安全管理器标记为弃用,并在将来的版本中移除。

JEP 412:外部函数与内存 API(第二次孵化)

传统上,Java 使用 Java Native Interface (JNI) 来与本地代码互操作。虽然功能强大,但 JNI 使用复杂且易出错,且性能开销较大。

Java 17 引入一套新的 API,使得 Java 程序能够安全、高效地调用外部函数(如 C 语言函数)和操作外部内存。API 包括:

  • 引入了外部函数接口,允许 Java 程序调用 C 语言等外部语言编写的函数。
  • 引入了外部内存接口,允许 Java 程序安全地访问和操作本地内存。
Java 版本 更新类型 JEP 更新内容
Java 14 孵化器 JEP 370 引入了外部内存访问 API
Java 15 第二孵化器 JEP 383 优化外部内存访问 API
Java 16 孵化器 JEP 389 引入了外部链接器 API
Java 16 第三孵化器 JEP 393 继续优化
Java 17 孵化器 JEP 412 引入了外部函数和内存 API

JEP 414:向量 API(第二次孵化)

向量 API 是在 Java 16 中作为孵化器引入的,主要是解决 Java 在进行高性能数值计算时的性能限制。Vector API 通过提供一套标准的 API,使得 Java 程序能够以一种高效、简洁且跨平台一致的方式进行向量计算。

Java 17 基于此前版本的反馈和经验进行了改进,第二次孵化。

Java 版本 更新类型 JEP 更新内容
Java 16 第一次孵化 JEP 338 提供一个平台无关的方式来表达向量计算,能够充分利用现代处理器上的向量硬件指令。
Java 17 第二次孵化 JEP 414 对 API 进行了改进,增加了性能优化和新的功能。

Java 17 新特性— 模式匹配 的Switch 表达式

img

Switch 表达式

Java 12 引入 Switch 表达式,它解决了传统 Switch 语句的两个缺陷:

  1. “Fall-through” 行为:在没有显式 break 语句的情况下,Switch 语句会从一个 case “穿透” 到下一个 case,忽略了这个会导致不可饶恕的错误。
  2. 代码冗余:每个 case,我们都需要重复类似的代码结构,增加了代码的冗余和维护难度。

Switch 表达式引入了 -> 操作符,用于替代传统的冒号(:)。与传统的 Switch 语句不同,使用 -> 的 case 分支不会出现 "fall-through" 现象,因此不需要 break 语句来防止穿透,如下:

    public static String getTypeOfDay(String day) {
        String typeOfDay = switch (day) {
            case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday";
            case "SATURDAY", "SUNDAY" -> "Weekend";
            default -> "Unknown";
        };
        return typeOfDay;
    }

它虽然解决这两个问题,但是还是有一个不好的地方,就是返回值,比如在处理复杂的逻辑时,仍需依赖外部变量来返回结果,所以 Java 13 对 Switch 表达式进行了扩展,引入 yield关键字来处理多分支结构中的返回值,如下:

    public static String getTypeOfDay(String day) {
        return switch (day) {
            case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" ->  "Weekday";
            case "SATURDAY", "SUNDAY" ->  "Weekend";
            default -> {
                if (day.isEmpty()) {
                   yield "day is empty";
                } else {
                    yield "Unknown";
                }
            }
        };
    }

但是,不知道小伙伴们注意没有,Switch 表达式只有一种类型,如果我们要处理多种类型呢?我们只能这样处理 :

Object obj = ... // 某个对象

switch (obj.getClass().getName()) {
    case "java.lang.String":
        String str = (String) obj;
        // 处理字符串
        break;
    case "java.lang.Integer":
        Integer i = (Integer) obj;
        // 处理整数
        break;
    case "com.lang.Long":
        Long lon = (Long) obj;
        // 处理MyClass实例
        break;
    // 其他类型
}

这里面的强制转换是不是比较烦?而且有没有很熟悉 ?Java 14 引入的模式匹配是不是可以解决?

模式匹配

instanceof 用于检查一个对象是否是特定类的实例或者该类的子类的实例。它通常用在条件语句中,以确定对象的类型,从而避免在向下转型时发生 ClassCastException。在 Java 14 前,我们一般都是这样写:

if (object instanceof String) {
    // 返回 true,确认是 String 类型,强制转换为 String 类型后使用
    String str = (String) object;
}

if 语句里面的强制转换显得很是多余,所以 Java 14 引入模式匹配的 instanceof 来解决这个问题,它允许在 instanceof 操作符的条件判断中直接定义一个变量,如果对象是指定的类型,这个变量会被初始化为被检查的对象,可以立即使用,无需额外的类型转换,如:

if (obj instanceof String str) {
    // 可以直接使用 str,而不需要显式的类型转换
}

但是这里需要写大量的 if...else,如果它和上面的 Switch 表达式相结合是不是就可以产生不一样的化学反应?

Switch 模式匹配

用于 instanceof 的模式匹配在Java 16 成为正式特性,到了 Java 17 模式匹配的应用扩展到了switch表达式,这标志着 Switch 表达式又得到了一次增强。

在 Java 17 中,switch 表达式允许使用模式匹配来处理对象类型,这样就可以直接在 switch 语句中检查和转换类型,而不需要额外的 if...else 结构和显式类型转换。

switch 模式匹配支持以下几种模式:

  1. 类型模式
  2. 空模式
  3. 守卫模式
  4. 常量模式

类型模式

这是一种比较常见的模式,它允许在 switch 语句的 case 分支中直接匹配对象的类型。例如,case String s 允许你在该分支中直接作为字符串类型的 s 来使用,避免了显式的类型检查和强制类型转换。举个例子来说明下:

    @Test
    public void switchTest() {
        Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
        for (Object obj: objects) {
            if (obj instanceof Integer intR) {
                System.out.println("为整数型:" + intR);
            } else if (obj instanceof Float floatR) {
                System.out.println("为浮点型:" + floatR);
            } else if (obj instanceof Double doubleR) {
            System.out.println("为双精度浮点数:" + doubleR);
            } else if (obj instanceof String str) {
                System.out.println("为字符串:" + str);
            } else {
                System.out.println("其他类型:" + obj);
            }
        }
    }

我们用 Switch 表达式来改造下:

    @Test
    public void switchTest() {
        Object[] objects = { "Hello", 123, "World", "Java", 3.14, "skjava" };
        for (Object obj: objects) {
            switch (obj) {
                case Integer intR -> System.out.println("为整数型:" + intR);
                case Float floatR -> System.out.println("为浮点型:" + floatR);
                case Double doubleR -> System.out.println("为双精度浮点数:" + doubleR);
                case String str -> System.out.println("为字符串:" + str);
                default -> System.out.println("其他类型:" + obj);
            }
        }
    }

相比上面的 if...else 简洁了很多。同时在 Java 17 之前,Switch 选择器表达式只支持特定类型,即基本整型数据类型byteshortcharint;对应的装箱形式ByteShortCharacterIntegerString

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Archie_java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值