Java23新特性(29岁的Java都更新啥)

4,468 阅读14分钟
日期更新说明
2025年1月4 日初版发布
2025年1月5日更新部分内容,调整格式、附加 GitHub 链接

前言

自从 Java 改为 6 个月更新以后让人感觉 现在的 Java 让人陌生(Java:你用 Java8 怪我咯)

下图是 Java 的未来更新记录,今天要介绍的内容主要针对 Java23 ,Java23 作为非 LTS 版本,更新的特性不是很多,但是 已经年近 29 岁的 Java 还在持续更新,甚至她慢慢的变得简洁了。

附加 Oracle 的 Jdk 的版本路线图

新特新总览

关于 Jdk23 的官方文档:openjdk.org/projects/jd…;个人想要详细解读,一定要解读原版文档,本文只是对他的摘要和带你快速了解新特性;帮省去更多时间。

JEP特性名称状态说明
455模式中的原始类型、instanceofswitch预览扩展模式匹配,允许在所有模式上下文中使用原始类型,并在 instanceofswitch中支持原始类型
466类文件 API第二次预览提供用于解析、生成和转换 Java 类文件的标准 API,简化对类文件的操作
467Markdown 文档注释正式发布允许在 JavaDoc 中使用 Markdown 语法编写文档注释,提高文档的可读性和编写效率
469向量 API第八次孵化提供用于表达向量计算的 API,支持在运行时编译为最优的向量指令,提升数值计算性能
473流收集器第二次预览增强流 API,支持自定义中间操作,使流管道能够以更灵活和富有表现力的方式转换数据
471弃用 sun.misc.Unsafe中的内存访问方法正式发布计划移除 sun.misc.Unsafe中的内存访问方法,鼓励开发者迁移到更安全的替代 API
474ZGC:默认的分代模式正式发布将 Z 垃圾收集器(ZGC)的默认模式切换为分代模式,以提升垃圾回收性能
476模块导入声明预览引入模块导入声明,允许开发者简洁地导入模块导出的所有包,简化模块化库的重用
477隐式声明的类和实例主方法第三次预览允许在没有显式声明类的情况下编写主方法,简化初学者的编程体验
480结构化并发第三次预览通过引入结构化并发,将相关任务视为单个工作单元,简化并发编程中的错误处理和取消操作
481作用域值第三次预览提供一种在线程内和线程之间共享不可变数据的机制,适合大量虚拟线程的场景
482灵活的构造函数体第二次预览允许在构造函数中,在显式的 super()this()调用之前出现语句,增强构造函数的灵活性

详细介绍

语言特性

JEP 455:Primitive Types in Patterns, instanceof, and switch(预览版)

模式中的原始类型、instanceof 和 switch(预览):JEP 455(预览版)旨在增强 Java 的模式匹配功能,允许在所有模式上下文中使用原始类型,并扩展 instanceof 和 switch 以支持所有原始类型。

先看示例吧(show your code):

private static void printValue(Object obj) {
    switch (obj) {
        case int i when i > 40 && i < 60 -> System.out.println("i is between 40 and 60");
        case int i -> System.out.println("i = " + i);
        case String s -> System.out.println("s = " + s);
        default -> System.out.println("obj is neither an Integer nor a String");
    }
}

点评: 惊奇发现 Java 突然对于 instanceof``switch的简化,代码的表现力更加简洁。

JEP 476:Module Import Declarations(预览版)

模块导入声明:JEP 476 提议在 Java 编程语言中引入模块导入声明,使开发者能够简洁地导入模块所导出的所有包,从而简化模块化

点评:对于初学者简化了导包的烦恼,其实更多是 Java 的再模块化新的探索,毕竟隔壁rust的模块可以按需引入,同时未来的 Java的模块化也是考虑到 Java 的包体积问题吧,对于开发者来说省去导入包和包冲突的烦恼。

JEP 477:Implicitly Declared Classes and Instance Main Methods(第三预览版)

JEP 477:隐式声明的类和实例主方法:JEP 477 提议在 Java 编程语言中引入隐式声明的类和实例 main 方法,旨在使初学者能够编写简洁的程序,而无需理解为大型程序设计的语言特性。

直接看图吧:

public class App 
{
    void main() {
        System.out.println("Hello world!");
    }
}

简单的说,就是简化main方法的写法哈,之前看到又博文嘲笑了这个更新了;这是一个很有意思的更新,简化了 main 方法入口函数编写;官方的说法是降低初学者的学习负担,同时让程序更快编写启动,甚至可以直接在jshell运行起来

JEP 482:Flexible Constructor Bodies(第二预览版)

JEP 482 提议在 Java 编程语言中允许构造函数体中的语句可以出现在显式构造函数调用(如 super(..) 或 this(..))之前。这些语句不能引用正在构建的实例,但可以初始化其字段。在调用其他构造函数之前初始化字段,使得在方法被重写的情况下,类的构造函数更加可靠。

class Animal {
    String name;

    public Animal(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    int age;

    public Dog(int age) {
        // 在Java23以前的版本中,调用父类的构造器(super())必须是构造函数的第一个语句,否则编译器会报错。
        this.age = age;
        // 甚至可以加上一些必要的参数校验
        if(age < 0) {
            throw new IllegalArgumentException("Age cannot be negative.");
        }
        super("Dog");
    }
}

需要注意的 Java 只是调整语法糖,但它不会改变或影响内部 JVM 指令,此新功能的 JVM 指令没有变化,因为构造函数的执行顺序仍然保持不变 - 从基类到派生类。此外,在super()执行之前,此功能仍然不允许您使用派生类实例的成员。更多的内容参考Java 22 中的构造函数改造

JEP 466:Class-File API(第二预览版)

JEP 466:类文件 API(第二次预览):旨在提供一个用于解析、生成和转换 Java 类文件的标准 API。

随着 JDK 的六个月发布周期,类文件格式演变的频率增加,现有的第三方库(如 ASM、BCEL 等)可能无法及时跟上这些变化。这导致框架开发者在处理比其捆绑的类文件库更新的类文件时,可能遇到错误或需要进行不安全的假设。因此,需要一个与 JDK 同步更新的标准类文件库,以确保兼容性和可靠性。

通过引入 Class-File API,JEP 466 使得 Java 开发者能够更方便地处理类文件,确保与最新的类文件格式兼容,并减少对第三方库的依赖。

备注:在后续章节会再次介绍,Java 封装字节码,减少了三方库的对于字节码的适配难度和升级带来的不兼容问题。

JEP 469:Vector API (八次孵化阶段)

JEP 469(第八次孵化)旨在引入一个 API,用于表达向量计算,使其在运行时可靠地编译为支持的 CPU 架构上的最优向量指令,从而实现比等效标量计算更高的性能。

Vector API 主要是利用 SIMD 指令以高效方式操作数据块的批量操作(向量化)提高计算密集型任务的性能 。主要对于以下场景有提升:

  • 科学计算: 如矩阵乘法、大规模数据处理、统计计算。
  • 多媒体处理:如图像处理、音频处理、视频编码/解码。
  • 加密和解密: 如高性能的加密算法(AES、SHA)实现。
  • 数据分析: 大规模数据筛选、聚合等操作。

JEP 473:Stream Gatherers(第二预览版)*

JEP 473(第二次预览)旨在增强 Stream API,以支持自定义的中间操作,从而使流管道能够以现有内置中间操作难以实现的方式来转换数据。

为什么要引入这个功能:

Java 8 引入了 Stream API,提供了一组固定的中间和终端操作,如映射、过滤、归约、排序等。然而,对于某些复杂任务,这些固定的中间操作可能不足以表达所需的流处理逻辑。例如,假设需要对字符串流进行去重,但基于字符串长度而非内容的唯一性。现有的 distinct 操作基于对象的相等性,无法满足这一需求。为此,需要引入自定义的中间操作,以实现更灵活的流数据转换。

目标:

  • 提高流管道的灵活性和表达能力:允许开发者定义自定义的中间操作,以满足特定的数据处理需求。
  • 支持对无限大小的流进行操作:确保自定义的中间操作能够有效地处理无限流。

JEP 480:Structured Concurrency(第三预览版)*

JEP 480 提议在 Java 编程语言中引入结构化并发的 API,以简化并发编程。结构化并发将一组在不同线程中运行的相关任务视为一个工作单元,从而简化错误处理和取消操作,提高可靠性,并增强可观察性。

为什么要引入这个功能:

在传统的非结构化并发模型中,开发者需要手动管理线程的生命周期、错误处理和取消操作,这增加了代码的复杂性和出错的风险。结构化并发通过将相关任务作为一个整体进行管理,确保任务的启动和终止具有明确的结构,减少了线程泄漏和取消延迟等常见问题,提高了代码的可维护性和可靠性。

目标:

  • 推广结构化并发编程风格:通过引入新的 API,鼓励开发者采用结构化并发的方式编写代码,减少并发编程中的常见错误。
  • 提高并发代码的可观察性:提供更好的工具和方法来监控并发任务的执行状态,便于调试和性能优化。

JEP 481:Scoped Values(第三预览版)*

JEP 481 提议在 Java 编程语言中引入作用域值(Scoped Values),使方法能够在同一线程内与其被调用的方法以及子线程共享不可变数据。与线程局部变量相比,作用域值更易于理解,并且在与虚拟线程(JEP 444)和结构化并发(JEP 480)结合使用时,具有更低的空间和时间成本。

为什么要引入这个功能:

在传统的 Java 并发编程中,线程局部变量(ThreadLocal)用于在同一线程内共享数据。然而,线程局部变量存在一些缺点,例如可变性、不受限制的生命周期以及在多线程环境下的性能开销。作用域值通过提供不可变且受限生命周期的数据共享机制,解决了这些问题,提高了代码的可维护性和性能。

目标:

易用性:使数据流更易于理解。

可理解性:共享数据的生命周期应从代码的语法结构中清晰体现。

健壮性:调用者共享的数据应仅能被合法的被调用者获取。

性能:应能够在大量线程之间高效地共享数据。

性能和运行时更新

JEP 474:ZGC:Generational Mode by Default

JEP 474 提议在 Java 23 中将 Z 垃圾收集器(ZGC)的默认模式切换为分代模式,并弃用非分代模式,计划在未来的版本中移除。

为什么要引入这个功能:

维护非分代 ZGC 会减缓新特性的开发。正如 JEP 439 所述,分代 ZGC 应该在大多数用例中优于非分代 ZGC。因此,最终用分代 ZGC 取代非分代 ZGC 可以降低长期的维护成本。

目标:

  • 聚焦分代 ZGC 的未来开发:通过将默认模式切换为分代模式,表明未来的开发将专注于分代 ZGC。
  • 减少维护成本:支持两种不同模式会增加维护成本,切换默认模式并弃用非分代模式有助于降低这些成本。

简要使用示例:

在此更改之前,启用 ZGC 的默认模式是非分代模式,需要显式指定分代模式:

java -XX:+UseZGC -XX:+ZGenerational ...

在应用 JEP 474 后,默认模式将是分代模式,因此只需启用 ZGC 即可:

java -XX:+UseZGC ...

如果仍需使用非分代模式,可以显式禁用分代模式,但会收到弃用警告:

java -XX:+UseZGC -XX:-ZGenerational ...

请注意,使用 -XX:-ZGenerational 将启用非分代 ZGC,并会收到以下警告:

警告: 选项 ZGenerational 已被弃用,将在未来版本中移除。 警告: 非分代模式已被弃用,计划在未来版本中移除。

因此,建议迁移到分代 ZGC,以确保未来版本的兼容性和性能优化。

工具

JEP 467:Markdown Documentation Comments

JEP 467 Markdown 文档注释:JEP 467 引入了在 JavaDoc 文档注释中使用 Markdown 语法的功能,使得文档注释的编写和阅读更加简洁易懂。

其他 JEP 471:Deprecate the Memory-Access Methods in sun.misc.unsafe for Removal

JEP 471 提议将 sun.misc.Unsafe 类中的内存访问方法标记为弃用,计划在未来的 JDK 版本中移除这些方法。这些不受支持的方法已被标准 API 所取代,主要包括在 JDK 9 中引入的 VarHandle API(JEP 193)和在 JDK 22 中引入的外部函数与内存 API(JEP 454)。我们强烈建议库开发者从 sun.misc.Unsafe 迁移到受支持的替代方案,以便应用程序能够顺利迁移到现代 JDK 版本。

为什么要引入这个功能:

sun.misc.Unsafe 类于 2002 年引入,旨在让 JDK 内部的 Java 类执行低级操作。然而,其内存访问方法可能导致未定义行为,包括 JVM 崩溃。因此,这些方法并未作为标准 API 暴露。随着时间的推移,已经引入了安全且高效的标准 API 来替代这些不安全的方法。因此,现在是时候弃用并最终移除 sun.misc.Unsafe 中的内存访问方法。

目标:

  • 为未来的移除做准备:为在未来的 JDK 版本中移除 sun.misc.Unsafe 中的内存访问方法做好准备。
  • 提高开发者意识:帮助开发者意识到他们的应用程序何时直接或间接地依赖于 sun.misc.Unsafe 中的内存访问方法。

总结:

Java23 的更新虽然是小版本更新,但对 java34 岁的年纪来说,保持这个更新;同时和新语言靠齐的情况下,他其实变得更新轻量化和活力;也开始参考和其他语言的特性。

上面的代码请看链接:github.com/will-we/blo…