现在 Java 23 的功能已经完成(在撰写文章的当天是第二阶段的第二阶段),是时候看看这个新版本为我们(开发人员)带来的功能了。
本文是有关 Java 最新版本的新功能的系列文章的一部分,对于那些想阅读这些文章的人,这里有链接:Java 22、Java 21、Java 20、Java 19、Java 18、Java 17 、Java 16、Java 15、Java 14、Java 13、Java 12、Java 11、Java 10 和 Java 9。
首先,据我所知,有一个事件在 Java 版本中从未发生过,预览中的一个功能被删除了!在 Java 21 中作为预览功能出现的字符串模板功能已被删除,将对该功能进行全局重新设计,因为它引起了很多争论并且似乎没有满足社区的期望。
这 给 12 它定义了预览功能流程,明确指出预览功能可以由功能管理员自行决定删除,而不需要新的 JEP。
最终,JEP 所有者必须决定预览功能的命运。如果决定删除预览功能,则所有者必须在 JBS 中提交问题以在下一个 JDK 功能版本中删除该功能;不需要新的 JEP。
这就是这里所做的。
有关删除票证中字符串模板的更多信息 JDK-8329949。
JEP 455 – 模式中的原始类型、instanceof 和 switch(预览)
预览版中的功能增加了对基本类型的支持 instanceof
和 switch
,并丰富模式匹配以支持原始类型模式: instanceof
,在盒子里 switch
,并在记录的解构中。
开关现在支持所有原始类型。
例子 :
long l = ...; switch (l) { case 1L -> ...; case 2L -> ...; case 10_000_000_000L -> ...; default -> ...; }
我们现在可以使用 instanceof
对于所有原始类型。
JEP 的示例:
if (i instanceof byte) { // value of i fits in a byte ... (byte)i ... // traditional cast required }
但最有趣的是对模式匹配的支持,这里是现在可以对原始类型执行模式匹配的每个地方的示例。
基本类型的模式匹配示例 switch
我拉了JEP:
switch (x.getStatus()) { case 0 -> "okay"; case 1 -> "warning"; case 2 -> "error"; case int i -> "unknown status: " + i; }
守卫也通过该条款得到支持 when
:
switch (x.getStatus()) { case 0 -> "okay"; case 1 -> "warning"; case int i when i > 1 && i < 100 -> "client error: " + i; case int i when i > 100 -> "server error: " + i; }
基本类型的模式匹配示例 instance
我拉了JEP:
if (i instanceof byte b) { ... b ... }
解构记录时基本类型的模式匹配示例:
// JSON didn't differentiates integers to doubles, so a JSON number should be a double. record JsonNumber(double number) implements JsonValue { } var number = new JsonNumber(30); // Previously we can only deconstruct this record via a it's exact component type: long, // now we can deconstruct and match a different priitive type if (json instanceof JsonObject(int number)) { // ... }
这种演变需要在模式匹配中实现转换规则,以便一个基本类型匹配另一个基本类型,如前面的示例 30 匹配一个 int
即使记录组件被定义为双精度型,模式测试也必须覆盖目标类型。这里 30 已经被 int 很好地覆盖了。未发现的值将被拒绝。
更多信息请参见 给予 455。
JEP 467 – Markdown 文档注释
允许您编写文档注释的功能 Java文档 在 Markdown 并且不再仅使用 HTML 标记和 JavaDoc 标记的混合。
在没有渲染的情况下,编写 HTML 代码并不总是那么容易且可读性很高,并且 JavaDoc 标记有时使用起来很复杂。 Markdown 是一种无需渲染即可阅读且易于使用的语言。将它用于 JavaDoc 注释是一个很好的选择。 Markdown 支持使用 HTML 标签,提供了极大的灵活性,如有必要,仍然支持 JavaDoc 特定标签。
Markdown 注释以 3 个斜杠开头: ///
。
以下是 JEP 中的示例:
/** * Returns a hash code value for the object. This method is * supported for the benefit of hash tables such as those provided by * {@link java.util.HashMap}. ** The general contract of {@code hashCode} is: *
- 在 Java 应用程序执行期间,每当对同一对象多次调用该方法时,{@code hashCode} 方法必须始终返回相同的整数,前提是对象上 {@code equals} 比较中使用的信息没有被修改。此整数不必在应用程序的一次执行和同一应用程序的另一次执行之间保持一致。
- 如果两个对象根据 {@link * #equals(Object) equals} 方法相等,则对两个对象中的每一个调用 {@code * hashCode} 方法必须产生 * 相同的整数结果。*
- 如果两个对象根据 {@link #equals(Object) equals} 方法不相等,则不要求对这两个对象分别调用 {@code hashCode} 方法必须产生不同的整数结果。但是,程序员应该意识到,对不相等的对象产生不同的整数结果可能会提高哈希表的性能。
*
* * @implSpec * 在合理实用的范围内,由类 {@code Object} 定义的 {@code hashCode} 方法将为不同的对象返回不同的整数。 * * @return 此对象的哈希码值。 * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode */
在 Markdown 中可以这样写:
/// Returns a hash code value for the object. This method is /// supported for the benefit of hash tables such as those provided by /// [java.util.HashMap]. /// /// The general contract of `hashCode` is: /// /// - Whenever it is invoked on the same object more than once during /// an execution of a Java application, the `hashCode` method /// must consistently return the same integer, provided no information /// used in `equals` comparisons on the object is modified. /// This integer need not remain consistent from one execution of an /// application to another execution of the same application. /// - If two objects are equal according to the /// [equals][#equals(Object)] method, then calling the /// `hashCode` method on each of the two objects must produce the /// same integer result. /// - It is _not_ required that if two objects are unequal /// according to the [equals][#equals(Object)] method, then /// calling the `hashCode` method on each of the two objects /// must produce distinct integer results. However, the programmer /// should be aware that producing distinct integer results for /// unequal objects may improve the performance of hash tables. /// /// @implSpec /// As far as is reasonably practical, the `hashCode` method defined /// by class `Object` returns distinct integers for distinct objects. /// /// @return a hash code value for this object. /// @see java.lang.Object#equals(java.lang.Object) /// @see java.lang.System#identityHashCode
更多信息请参见 给予 467。
JEP 471 – 弃用 sun.misc.Unsafe 中的内存访问方法并将其删除
不安全 顾名思义,它是 JDK 的一个内部且不受支持的类,调用起来不安全。由于历史原因,许多低级框架使用 Unsafe 来实现更快的内存访问。感谢功能 VarHandle API (给予 193,自 Java 9 起)和 外部函数和内存 API (给予 454,自 Java 22 起),现在有访问内存的不安全方法的替代品,这些方法同样高效,但更安全和受支持。总共,Unsafe 中包含的 87 个方法中有超过 79 个受到影响,这让我们更接近整个类可能被弃用然后删除的时刻!
弃用这些方法清楚地表明是时候使用这些替代方法了!然而,我们大多数人不应该看到这些变化,因为除了框架或库之外,Unsafe 很少在任何地方使用。
这些方法会被降级,然后逐渐贬值:
- 第 1 阶段:Java 23 中的弃用(此 JEP)
- 第 2 阶段:在 Java 24 或 25 中使用时在运行时记录警告
- 第 3 阶段:在 Java 26 或更高版本中默认生成异常(可通过命令行选项修改行为)
- 阶段 4 和阶段 5:Java 26 之后删除方法(先是堆内访问方法,然后是堆外内存访问方法)
更多信息请参见 给予 471。
JEP 474 – ZGC:默认采用分代模式
ZGC 是一个垃圾收集器,旨在支持非常大的堆(几 TB)和非常小的暂停(大约一毫秒)。
通过以下方式在 Java 21 中添加分代堆 给予 439 允许它支持不同的工作负载,同时消耗更少的资源。
分代模式现在是默认模式。
更多信息请参见 给予 474。
JEP 476 – 模块导入声明(预览)
在 Java 中,可以导入:
- 带语句的包中的所有类
import java.util.*;
- 有指导的课程
import java.util.Map;
- 类的所有方法和静态变量都用该声明
import static org.junit.jupiter.api.Assertions.*;
- 带有声明的静态方法或变量
import static org.junit.jupiter.api.Assertions.assertTrue;
但是不可能在一条指令中导入模块的所有类,这是通过指令完成的 import module java.base;
这将在一条语句中导入从模块导出的所有包的所有类 java.base
以及那些通过传递所需的模块 java.base
。
更多信息请参见 给予 476。
预览版中的功能
Java 23 中的预览版或孵化器尚未发布之前预览版(或孵化器模块)中的任何功能。
当然,除了我在简介中谈到的字符串模板功能,该功能使预览无处可去,因为它已被删除。
仍处于预览状态的功能
以下功能仍处于预览状态(或孵化器模块)。
- 给予 466 – 类文件API :第二个预览版,用于解析、生成和转换 Java 类文件的标准 API。根据使用反馈进行改进。值得注意的是,JDK 向这个新 API 的迁移在 Java 23 中继续进行。
- 给予 469 – 矢量API :第八次孵化,用于表达向量计算的 API,这些计算在运行时编译为支持的 CPU 架构的向量指令。没有变化,JEP 中指出,只要 Valhalla 项目的功能在预览版中不可用,Vector API 就会处于孵化状态。这是预料之中的,因为 Vector API 随后将能够利用 Valhalla 项目应带来的性能和内存表示改进。
- 给予 473 – 溪流收集者 :第二个预览版,通过支持自定义中间操作丰富了 Stream API。没有变化。
- 给予 477 – 隐式声明的类和实例主要方法 :第三个预览版,通过允许在隐式类(无需声明)和实例方法中定义简单程序来简化简单程序的编写
void main()
。两个变化:隐式类自动导入 3 个静态方法print(Object)
,println(Object)
等readln(Object)
新班级的java.io.IO
,并且它们会根据需要自动导入模块包中的类java.base
。 - 给予 480 – 结构化并发 :第三个预览版,新的 API 通过允许将多个并发任务视为单个处理单元来简化多线程代码的编写。没有变化。
- 给予 481 – 范围值 :第三个预览版,允许在线程内和线程之间共享不可变数据。一个小小的改变。
- 给予 482 – 灵活的构造函数主体 :第二次预览,允许指令的功能 前卫 只要它们不访问正在创建的实例,就会调用父构造函数。现在,构造函数可以在显式调用构造函数之前初始化同一类的字段。
关于这些的详细内容,你可以参考我之前的文章。
潜水员
JDK 的各种补充:
- 继 给予 477 – 隐式声明的类和实例主要方法,除了新班级
IO
,同样的3个方法已添加到类中Console
:print(Object)
,println(Object)
等readln(Object)
。 - 班级
Console
添加了 3 个新方法,允许在区域设置中使用格式化字符串:format(Locale, String, Object)
,printf(Locale, String, Object)
等readLine(Locale, String, Object)
。 Console.readPassword(Locale, String, Object)
: 等同于Console.readPassword(String, Object)
而是将语言环境作为字符串位置的参数。Inet4Address.ofPosixLiteral(String)
:创建一个Inet4Address
基于以 POSIX 兼容形式提供的 IPv4 地址的文本表示 inet 地址。java.text.NumberFormat
及其后代看到了方法的增加setStrict(boolean)
等isScript()
它允许您更改格式化模式,默认模式是严格的。Instant.until(Instant)
: 计算Duration
直到另一个Instant
。
以下方法已被删除,它们已被弃用以进行删除,并且在先前版本中已弃用以抛出异常:
Thread.resume()
等Thread.suspend()
。ThreadGroup.resume()
,ThreadGroup.stop()
等ThreadGroup.suspend()
。
所有新的 JDK 23 API 都可以在以下位置找到: Java 版本年鉴 – Java 23 中的新 API。
内部变更、性能和安全性
并行 GC 垃圾收集器重新实现了其 Full GC 算法,以使用更经典的类型算法 并行标记-清除-压缩。这与 G1 垃圾收集器使用的相同,它优化了某些特定情况下的性能,并在使用并行 GC 时将堆使用率减少了 1.5%。垃圾收集器方面还进行了其他更改,您可以在 Thomas Schatzl 的这篇文章中找到它们: JDK 23 G1/并行/串行 GC 变化。
我还没有注意到任何其他显着的变化,但如果我发现更多,我会更新这篇文章。
JFR 活动
没有新的 Java Flight Recorder (JFR) 事件。
您可以在页面上找到该版本的 Java 支持的所有 JFR 事件 JFR 活动。
结论
这个新版本的 Java 在新功能方面相当稀疏,并且预览版中发布的正在开发的功能也很少。
模式匹配中对基本类型的支持以及 JavaDoc 中对 Markdown 的支持仍然是非常有趣的改进,但字符串模板的消失而没有替代表明对这一备受期待的功能的支持还需要相当长的一段时间。
要查找 Java 23 中的所有更改,请参阅 发行说明。