当前位置:网站首页 > Java教程 > 正文

java23教程



在这里插入图片描述

Java 23 自 2024 年 6 月 6 日起进入所谓的“Rampdown 第一阶段”,因此该版本中将不再包含任何 JDK 增强提案 (JEP)。因此,功能集已修复。只会纠正错误,并且如有必要,将进行微小改进。

目标发布日期为 2024 年 9 月 17 日。您可以在此处下载最新的抢先体验版本。
在这里插入图片描述
Java 23 的亮点:

  • 文档注释:最后,我们可以用编写。
  • 使用Patterns、instanceof和switch中的基本数据类型primitive types将变量与基元类型进行匹配(预览)。
  • 使用模块导入声明()导入整个模块。直接使用:不需带和的输入和输出,带隐式声明的类和实例main方法。
  • 在调用之前,允许定义其它语句(即super不再是构造函数第一条语句)使用灵活的构造函数体初始化构造函数中的字段。

此外,和中引入的许多其他功能也正在进入新一轮预览,无论是否有细微变化。

Java 21 中引入并在 Java 22 中重新引入的字符串模板是个例外:它们不再包含在 Java 23 中。根据 Gavin Bierman 的说法,大家一致认为需要修改设计,但对于如何实际进行修改存在分歧。因此,语言开发人员决定花更多时间来修改设计,并在以后的 Java 版本中以完全修改的形式呈现该功能。

与往常一样,我对所有 JEP 和其他更改都使用原始英文标题。
在这里插入图片描述

为了格式化 JavaDoc 注释,我们一直必须使用 HTML。这在 1995 年无疑是一个不错的选择,但如今, 比 HTML 更受欢迎,更适合用于编写文档。

JDK 增强提案 467允许我们从 Java 23 开始在 Markdown 中编写 JavaDoc 注释。
在这里插入图片描述

以下示例以常规符号显示了该方法的文档:

 

该示例包含格式化的代码、段落标记、项目符号列表、链接以及 JavaDoc 特定信息,例如@param和@return。

要使用 Markdown,我们需要用开始 JavaDoc 注释的所有行。Markdown 中的相同注释如下所示:

 

这既更容易编写,也更容易阅读。

具体来说,有哪些变化?

  • 源代码以标记而不是{@code …}。
  • HTML 段落字符已被空行替换。
  • 枚举项以连字符引入。
  • 链接用 标记[…],而不是{@link …}。
  • JavaDoc 特定的细节(例如和)保持不变。

支持以下文本格式:

 

支持枚举列表和编号列表:

 

您还可以显示简单的表格:

 

您可以按照如下方式集成到其他程序元素的链接:

 

如果链接文本和链接目标不同,可以将链接文本放在前面的方括号中:

 

最后但同样重要的一点是,如果在代码或代码块中使用 JavaDoc 标签(例如@param、@throws等),则不会对其进行评估。

Java 23 引入了两个新的预览功能。您不应在生产代码中使用这些功能,因为它们仍可能发生变化(或者,就像字符串模板的情况一样,可能会在短时间内再次被删除)。

javac您必须通过 VM 选项在命令中明确启用预览功能–enable-preview --source 23。对于该java命令来说,–enable-preview这就足够了。

2.1 模块导入声明(预览)– JEP 476

从 Java 1.0 开始,java.lang包中的所有类都会自动导入到每个.java 文件中。这就是为什么我们可以不加语句地使用诸如Object、String、Integer、Exception、Thread等类import。

我们也一直能够导入完整的软件包。例如,导入java.util.*意味着我们不必单独导入和等类。

JDK 增强提案 476现在允许我们导入完整的模块- 更准确地说,是模块导出的包中的所有类

例如,我们可以按如下方式导入完整的java.base模块,并使用此模块中的类(在示例中),无需进一步导入:

 

要使用,导入类本身不需要位于模块中。

2.1.1 模糊的类名

如果有两个导入的类具有相同的名称,例如以下示例中的,则会发生编译器错误:

 

解决方案很简单:我们还必须直接导入所需的类:

 
2.1.2 过渡导入

如果导入的模块传递性地导入另一个模块,那么我们也可以使用传递性导入模块的导出包的所有类,而无需显式导入。

例如,java.sql模块以传递方式导入java.xml模块:

 

因此,在以下示例中,我们不需要SAXParserFactory和SAXParser的任何显式导入,也不需要java.xml`模块的显式导入:

 
2.1.3 JShell 中的自动模块导入

JShell会自动导入10个常用包。此JEP将使在将来导入完整的模块。

通过调用JShell一次不使用–enable-preview和一次使用–enable-preview,然后输入命令,可以很好地演示这一点:

 

当启动JShell而不启用预览()时,您将看到十个导入的包;当使用启动它时,您将只看到模块的导入。

2.1.4 隐式声明类中的自动模块导入

java.base从 Java 23 开始,隐式声明的类也会自动导入完整的模块。

2.2 模式中的原始类型、instanceof 和 switch(预览)– JEP 455

使用instanceof和switch,我们可以检查某个对象是否属于某种类型,如果是,则将该对象绑定到该类型的变量,执行特定的程序路径,并在该程序路径中使用新变量。

例如,以下代码块(自Java 16起已获准使用)检查对象是否为至少包含 5 个字符的字符串,如果是,则以大写形式打印。如果对象是整数,则计算该数字的平方并打印。否则,按原样打印对象。

 

从Java 21开始,我们可以使用switch更清楚地做到这一点:

 

然而,到目前为止,这只适用于对象。instanceof根本不能与基元数据类型一起使用,只在可以将基元类型byte、short、char和int的变量与常量匹配的范围内进行切换,例如:

 
  • 首先,所有基元类型现在都可以在switch表达式和语句中使用,包括long、float、double和boolean。
  • 其次,我们还可以在模式匹配中使用所有原始类型,包括instanceof和switch。

在这两种情况下,即对于通过long、float、double和boolean进行切换,以及与原始变量进行模式匹配,切换(与所有新的切换功能一样)必须是详尽的,即涵盖所有可能的情况。

2.2.1 来自 Java 23:模式匹配中的原始类型

使用原始模式时,确切的含义与使用对象时不同,因为原始类型没有继承:
是一个基本类型的变量(即或),B是这些基本类型之一。然后,如果a的精确值也可以存储在B类型的变量中,则B的实例结果为真。
为了帮助你更好地理解这是什么意思,这里有一个简单的例子:

 

代码应按如下方式读取:如果变量值的值也可以存储在字节变量中,则将此值分配给字节变量b并打印出来。
例如,对于值=5,情况就是这样,但对于值=1000则不然,因为字节只能存储-128到127之间的值。
与对象一样,对于基本类型,您还可以使用&&直接在check实例中添加进一步的检查。例如,以下代码仅打印正字节值(即1到127):

 

让我们为value分配一个具体值,并将其与所有数值基元类型进行比较(不允许将数值类型与布尔值进行比较,这会导致编译器错误):

 

此代码打印以下内容:

 
 
 
 

结果出乎意料:

 
 

此代码块打印以下内容:

 
 
2.2.2 带switch的基本类型模式

我们不仅可以在instanceof中使用原始模式,还可以在switch中使用:

 

以下是一些值的示例以及相应值将导致的输出:

  • 0可以表示为字节(-128到127)。
  • 10000可以表示为短(-32768到32767)。
  • 50000可以表示为一个char(0到65535)。
  • 可以表示为整数(-到)。
  • 00可以表示为一个长(大约负9万亿到正9万亿)。
  • 0.125可以表示为浮点数。
  • 另一方面,0.126只能表示为双精度。

同样,对于中的原始类型模式,我们可以使用“guards”,即用将布尔表达式附加到模式上:

 
2.2.3 支配型和支配型
 

在这种情况下,case字节标签永远不会匹配,因为每个字节也是一个int,因此已经由case int标签处理。一般来说,支配类型必须始终列在支配类型之前。因此,以下内容是可以的:

 
switch的疲劳检查

与Java 21中添加的所有开关功能一样,以下规则适用:如果您使用其中一个新功能,则switch必须详尽无遗。这就是为什么前面的例子也包含一个。以下行为是不允许的:

 
 

Java 23 中再次推出了 7 个预览和孵化器功能,其中 3 个与 Java 22 相比没有变化:

3.1 流收集器(第二预览版)– JEP 473

自从 Java 8 引入 Stream API 以来,Java 社区一直抱怨中间流操作的范围有限。诸如“窗口”或“折叠”之类的操作非常缺乏,而且反复被要求。

JDK 开发人员没有屈服于社区的压力而提供这些功能,而是有一个更好的想法:他们实现了一个 API,他们和所有其他 Java 开发人员可以使用它来自己实现中间流操作。

这个新 API 被称为“流收集器”。它首次由JDK 增强提案 461在Java 22中引入,并由JDK 增强提案 473在 Java 23 中第二次作为预览版未经更改地呈现,以便收集来自社区的进一步反馈。

例如,通过以下代码,我们可以实现并使用中间流操作“map”作为流收集器:

 

您可以在有关流收集器的主要文章中确切了解流收集器的工作原理、有哪些限制,以及我们是否最终会得到期待已久的“窗口”和“折叠”操作。

3.2 隐式声明的类和实例主要方法(第三个预览) - JEP 477

当 Java 开发人员编写他们的第一个程序时,它通常看起来像这样(直到现在)

 

Java 初学者会同时面临许多新概念:

通过classes.

  • 使用可见性修饰符public.
  • 使用静态方法.
  • 使用未使用的方法参数.
  • 和System.out。

如果我们可以摆脱所有这些并专注于基本要素,那不是很好吗 - 就像下面的截图所示?
在这里插入图片描述
这正是“隐式声明的类和实例主方法”所实现的!
从Java 23开始,以下代码是一个有效且完整的Java程序:

 

这是如何实现的?

  1. 指定类不再是强制性的。如果省略类规范,编译器将生成隐式类。
  2. 方法main()不必是public或static,也不必有参数
  3. 隐式类自动导入新类java.io.IO,其中包含静态方法print(…)、println(…)和readln(…)。
    有关更多详细信息、示例、需要遵守的限制以及main()重载多个方法时会发生什么情况,请参阅有关Java main() 方法的主要文章。

这里描述的更改最初在Java 21中以“未命名类和实例主方法”的名称发布。在Java 22中,该功能的一些过于复杂的方面得到了简化,并且该功能已重命名为当前名称。

在 Java 23 中,JDK 增强提案 477添加了自动导入的java.io.IO类,因此最终System.out也可以省略,这在 Java 22 的第二个预览版中还无法实现。

请注意,该功能在 Java 23 中仍处于预览阶段,必须使用 VM 选项激活–enable-preview。

3.3 结构化并发(第三次预览)– JEP 480

结构化并发是一种现代方法,通过虚拟线程将任务分成几个子任务以并行执行。

结构化并发为并行任务的开始和结束提供了清晰的结构,并有序地处理错误。如果不再需要某些子任务的结果,则可以干净地取消这些子任务。

使用结构化并发的一个例子是实现一种race()方法,该方法启动两个任务并返回完成的任务的结果,而另一个任务则自动取消:

 

您可以在有关结构化并发的主要文章中找到更详细的描述、更多用例和大量示例。

结构化并发是Java 21中的预览功能,并在Java 22中再次呈现,没有任何变化。Java 23 中也没有变化(由JDK 增强提案 480指定)——JDK 开发人员希望在最终确定该功能之前获得进一步的反馈。

3.4 作用域值(第三次预览) – JEP 481

作用域值可用于将值传递给远程方法调用,而无需将它们作为参数循环遍历调用链的所有方法。
经典的例子是用户登录到要为其执行特定用例的web服务器。作为此类用例的一部分,许多方法都需要访问用户信息。使用Scoped Values,我们可以设置一个上下文,在该上下文中,所有方法都可以访问用户对象,而无需将其作为参数传递给所有这些方法。
以下代码使用创建上下文:

 

现在,run(…)方法中调用的方法,以及它直接或间接调用的所有方法,例如调用堆栈中称为deep的存储库方法,都可以按如下方式访问用户:

 

在Java 23中,JDK增强建议481将ScopedValue类的以下两个静态方法合并为一个:

 
 

在 Java 22 中,我们必须按如下方式调用此方法:

 

由于Callable.call()抛出了通用的Exception,我们必须捕获Exception,即使被调用的方法抛出了更具体的异常。

在 Java 23 中,现在只有一种callWhere(…)方法:

 

现在将a传递给方法,而不是 aSupplier或 a 。这是一个函数式接口,定义如下:CallableScopedValue.CallableOp

 

这个新接口包含一个可能抛出的异常作为类型参数X。这允许编译器识别调用可能抛出哪种异常callWhere(…)——我们可以直接SpecificException在catch块中处理:

 

如果doSomethingSmart()没有抛出异常或者抛出了RuntimeException,我们可以省略 catch 块:

 

Java 23 中的这一变化使代码更具表现力,且更不容易出错。

3.5 灵活的构造函数主体(第二次预览)– JEP 482

假设您有如下类:

 

并且我们假设您有第二个扩展该类的类:

 

现在,您要确保在调用父类构造函数之前,构造函数ConstructorTestChild中的和不为负数。

以前不允许在构造函数之前进行相应的检查。这就是为什么我们不得不使用如下的扭曲方法:

 

这既不优雅也不容易阅读。

我们还假设您想要覆盖printMe()父类构造函数中调用的方法,以打印派生类的字段:

 

如果调用,此方法会打印什么new ConstructorTestChild(1, 2)?

它不会打印a = 1和b = 2,但是:

 

这是因为b此时尚未初始化。它仅在调用之后super(…)才初始化,即在构造函数之后,而构造函数又调用printMe()。

有了“灵活的构造函数主体”,这两个问题就都成为过去了。

将来,在调用父类构造函数之前,以及在调用重载构造函数之前 我们可以执行任何不访问当前构造的实例的代码,即不访问其字段(这在Java 22中已经通过JDK 增强提案 447实现)。

此外,我们还可以初始化刚刚构造的实例的字段。这在 Java 23 中通过JDK 增强提案 482实现。

这些更改现在允许将代码重写如下:

 

调用new ConstructorTestChild(1, 2)now 会产生预期的输出:

 

新代码更易于阅读且更安全,因为它降低了在派生类中重写方法访问未初始化字段的风险。

您可以在有关灵活构造函数主体的主要文章中找到更多需要考虑的示例和限制。

3.6 Class-File API(第二个预览版)– JEP 466

Java Class-File API 是用于读写.class 文件(即已编译的 Java 字节码)的接口。它旨在取代JDK 中广泛使用的字节码操作框架ASM 。

Class-File API 在Java 22中作为预览功能引入,并由JDK 增强提案 466在 Java 23 中进入第二轮预览,并进行了一些改进。

由于可能只有少数 Java 开发人员会直接使用 Class-File API,但通常通过其他工具间接使用,因此我不会在这里详细描述新的接口,就像在 Java 22 文章中一样。

如果您对 Class-File API 感兴趣,您可以在JDK 增强提案 466中找到所有详细信息。或者在文章下发表评论!如果出乎意料的是,有足够的兴趣,我很乐意写一篇关于 Class-File API 的文章。

3.7 Vector API(第八个孵化器)– JEP 469

Vector API 首次作为孵化器功能纳入 JDK 已有三年半时间。在 Java 23 中,它将保持孵化器阶段,不会发生任何变化,正如JDK 增强提案 469所规定的那样。

Vector API 可以将如下向量计算映射到现代 CPU 的特殊指令中。这将使此类计算能够以极快的速度执行 - 只需一个 CPU 周期即可达到一定的向量大小!
在这里插入图片描述
向量加法的示例

一旦 Vector API 进入预览阶段,我就会对其进行详细描述。这大概就是当Vector API 所需的Project Valhalla功能也在预览阶段可用时的情况(根据 Valhalla 开发人员大约一年前的声明,应该“很快”就会出现这种情况)。

在本节中,您将找到已从JDK 中标记为弃用或完全删除的功能的概述。
在这里插入图片描述

4.1 弃用 sun.misc.Unsafe 中的内存访问方法并将其删除 – JEP 471

该类于 2002 年随 Java 1.4 引入。其大多数方法允许直接访问内存 - 既可以访问 Java 堆,也可以访问不受堆控制的内存(即本机内存)。

正如类名所示,这些操作大部分都是不安全的。如果使用不当,可能会导致未定义的行为、性能下降或系统崩溃。

Unsafe 最初仅用于 JDK 内部目的,但在 Java 1.4 中,没有模块系统可以向我们开发人员隐藏此类,并且如果您想尽可能高效地实现某些操作(例如,比较和交换)或访问大于 2 GB 的堆外内存块(这是ByteBuffer的极限),则没有其他选择。

然而,今天我们有了其他选择:

  • Java 9 引入了VarHandles,它可以直接且优化地访问堆内存,可以设置各种类型的内存屏障,并提供比较和交换等原子操作。
  • 在Java 22中,外部函数和内存 API已完成。此 API 允许调用本机库中的函数并管理本机内存(即堆外内存)。

由于这些稳定、安全且性能卓越的替代方案的出现,JDK 开发人员决定在JDK 增强提案 471中将所有用于Unsafe访问堆上和堆外内存的方法标记为已弃用,并在 Java 23 中将其删除,并在未来的 Java 版本中删除它们。

拆除工作分四个阶段进行:

阶段 1:在 Java 23 中,这些方法被标记为已弃用并被删除,以便在使用时发出编译器警告。
阶段 2:据推测,在Java 25中,使用这些方法也会导致运行时警告。
阶段 3:据推测,在 Java 26 中,这些方法将抛出一个UnsupportedOperationException。
阶段 4:删除方法。尚未决定在哪个版本中执行此操作。
我们可以使用 VM 选项覆盖各个阶段的默认行为:

  • –sun-misc-unsafe-memory-access=allow – 可以使用所有不安全的方法。会显示编译器警告,但运行时不会发出任何警告(第 1 阶段的默认设置)。
  • –sun-misc-unsafe-memory-access=warn – 第一次调用受影响的方法之一时,运行时会显示警告(第 2 阶段的默认设置)。
  • –sun-misc-unsafe-memory-access=debug – 每当调用受影响的方法之一时,都会在运行时发出警告和堆栈跟踪。
  • –sun-misc-unsafe-memory-access=deny – 受影响的方法抛出(第 3 阶段的默认设置)。

在第2和第3阶段,只能激活前一阶段的行为,而在第4阶段,此VM选项将不再具有任何效果。

在JEP 的sun.misc.Unsafe 内存访问方法及其替代部分中可以找到标记为已弃用的所有方法及其各自替代的完整列表。

4.2 Thread.suspend/resume 和 ThreadGroup.suspend/resume 已被删除

容易发生死锁的方法Thread.suspend()、Thread.resume()、ThreadGroup.suspend()和已在 Java 1.2 中标记为弃用。ThreadGroup.resume()

在Java 14中,这些方法被声明为已弃用并被删除。

自Java 19起,ThreadGroup.suspend()和resume()抛出了UnsupportedOperationException– ;自Java 20起,Thread.suspend()和 也抛出了 – resume()。

在 Java 23 中,所有这些方法最终被删除。

此更改没有 JEP;它在JDK-下的错误跟踪器中注册。

4.3 ThreadGroup.stop 已被删除

另外,在 Java 1.2 中,ThreadGroup.stop()它被标记为已弃用,因为从一开始停止线程组的概念就没有得到很好的实现。

在Java 16中,该方法被声明为已弃用且应被删除。

从Java 19开始,ThreadGroup.stop()抛出一个UnsupportedOperationException。

该方法最终在 Java 23 中被删除。

此更改没有 JEP;它在JDK-下的错误跟踪器中注册。

在本节中,你会发现大多数 Java 开发人员在日常工作中不会遇到的变化。当然,了解这些变化还是有好处的。

5.1 ZGC:默认采用分代模式 – JEP 474

Java 21引入了 Z 垃圾收集器(ZGC)的“分代模式”。在此模式下,ZGC 使用“弱分代假设”,将新旧对象存储在两个单独的区域中:“年轻代”和“老一代”。年轻代主要包含短寿命对象,需要更频繁地清理,而老一代包含长寿命对象,需要清理的频率较低。

在 Java 21 中,必须使用 VM 选项激活代际模式。

由于代际模式在大多数情况下都可以显著提高性能,因此根据JDK 增强提案 474的规定,该模式在 Java 23 中默认激活。

这意味着 VM 选项会自动以分代模式激活 ZGC。

您可以使用 停用代际模式。

5.2 删除模块 jdk.random

此更改未归类到“删除”下,因为实际上未删除任何内容。jdk.random模块中的所有类都已移至java.base模块中。

如果您使用 Java 模块系统并且已requires jdk.random在某处指定,则可以在 Java 23 中删除此语句(java.base模块自动包含)。

此更改没有 JEP;它在JDK-下的错误跟踪器中注册。

5.3 具有显式区域设置的控制台方法

利用ConsoleJava 6引入的类,我们可以方便地将文本打印到控制台,并从控制台读取用户输入:

 
 

在此示例中,Pi 现在始终以美国格式打印,即 3.1415。

此更改没有 JEP;它在JDK-下的错误跟踪器中注册。

5.4 支持持续时间直至另一个瞬间

要确定两个对象之间的持续时间Instant,以前必须使用Duration.between(…):

 

由于这种方法不容易找到,因此Instant.until(…)引入了一种执行相同计算的新方法:

 

此更改没有 JEP;它在JDK-下的错误跟踪器中注册。

5.5 Java 23 中所有变更的完整列表

在本文中,您了解了 JDK 增强提案 (JEP) 带来的所有 Java 23 功能以及发行说明中的​​一些其他选定更改。您可以在Java 23 发行说明中找到所有更改的完整列表。

Java 23 为我们带来了三个新功能和许多更新的预览功能

  • 未来编写和阅读JavaDoc注释将更容易,因为我们现在也可以使用。
  • 我们还可以使用导入整个模块,而不是像以前那样只导入类和包,从而使的块更加清晰。
  • 基元类型模式通过基元类型扩展了的模式匹配功能。然而,我无法想象我们会在代码中大量使用这种模式匹配(与Java在以前版本中添加的模式匹配功能相反)。
  • 在隐式声明的类中,我们现在可以编写而不是。
  • 现在传递了一个类型化的,这样编译器就可以自动识别被调用的操作是否会抛出已检查的异常,如果是,是哪个异常。这意味着我们不再需要处理通用的Exception,而是处理实际抛出的Exception。因此,可以省略单独的方法。
  • 在派生类的构造函数中,我们现在可以在调用之前初始化派生类的字段。如果父类的构造函数调用在派生类中被覆盖的方法并访问这些字段,这将很有帮助。
  • 任何使用Z垃圾回收器的人在升级到Java 23时都会自动受益于新一代模式,使大多数应用程序的性能明显提高。
  • 还有一个主要的整理:、、、和方法,这些方法多年来一直被标记为不推荐使用,最终在Java 23中被删除。所有不安全的内存访问方法都已标记为不推荐删除。

您最期待 Java 23 的哪个功能?您最怀念哪个功能?通过评论区咱们一起讨论下吧!

版权声明


相关文章:

  • java设计与实践教程2025-01-09 14:42:03
  • 阿里java视频教程2025-01-09 14:42:03
  • java emma教程2025-01-09 14:42:03
  • java web开发教程pdf2025-01-09 14:42:03
  • java代码教程视频2025-01-09 14:42:03
  • java421教程全集2025-01-09 14:42:03
  • java开发教程视频教程2025-01-09 14:42:03
  • java腾讯视频教程2025-01-09 14:42:03
  • mysql使用教程 java2025-01-09 14:42:03
  • java引用对象教程2025-01-09 14:42:03