2024年9月java23发布啦!!!
JDK 23 提供了12 项增强功能,这些功能足以保证其自己的JDK 增强提案 - JEP ,其中包括 8 项预览功能和 1 项孵化器功能。它们涵盖了对 Java 语言、API、性能和 JDK 中包含的工具的改进。除了 Java 平台上的工作之外,Oracle JDK 23 中的 Oracle GraalVM JIT 编译器 (Graal JIT) 现在已包含在 Oracle JDK 中可用的 JIT 中.
JDK 23日程
2024/06/06 斜坡降级第一阶段 (主线分支) 2024/07/18 第二阶段降级 2024/08/08 初始候选版本 2024/08/22 最终候选版本 2024/09/17 全面上市
12 项增强功能
- 455:模式、instanceof 和 switch 中的原始类型(预览)
- 466:Class-File API(第二个预览版)
- 467:Markdown 文档注释
- 469:Vector API(第八孵化器)
- 473:流收集者 (第二版预览)
- 471:弃用 sun.misc.Unsafe 中的内存访问方法并将其删除
- 474:ZGC:默认采用分代模式
- 476:模块导入声明(预览)
- 477:隐式声明的类和实例主要方法(第三次预览)
- 480:结构化并发(第三次预览)
- 481:范围值(第三次预览)
- 482:灵活的构造函数主体(第二次预览)
JEP455: 模式中的原始类型、instanceof 和 switch(预览)
原文地址:JEP 455: Primitive Types in Patterns, instanceof, and switch (Preview)
通过允许在所有模式上下文中使用原始类型模式来增强模式匹配,并扩展instanceof和switch使用所有原始类型。这是一项预览语言功能。
- 通过允许所有类型(无 论是原始类型还是引用类型)的类型模式实现统一的数据探索。
- 将类型模式与 对齐instanceof,并instanceof与安全铸造对齐。
- 允许模式匹配在嵌套和顶级上下文中使用原始类型模式。
- 提供易于使用的构造,消除由于不安全的转换而丢失信息的风险。
- switch继Java 5(枚举switch)和 Java 7(字符串)中的增强之后switch,允许switch处理任何原始类型的值。
例如,即使存在潜在的损失,赋值语句也会自动将一个int
值转换为一个,并且开发人员不会收到任何警告:float
int getPopulation() {...}
// silent potential loss of information
float pop = getPopulation();
同时,将一个int
值转换为一个byte
是通过显式转换来完成的,但是转换可能会有损失,因此必须先进行费力的范围检查:
if (i >= -128 && i <= 127) {
byte b = (byte)i;
... b ...
}
instanceof
可以检查值和类型。上面的两个示例可以重写如下:
if (getPopulation() instanceof float pop) {
... pop ...
}
if (i instanceof byte b) {
... b ...
}
JEP466: Class-File API(第二个预览版)
原文地址:JEP 466: Class-File API (Second Preview)
提供用于解析、生成和转换 Java 类文件的标准 API
-
精简了
CodeBuilder
类。此类有三种用于字节码指令的工厂方法:低级工厂、中级工厂和用于基本块的高级构建器。根据反馈,我们删除了与低级方法重复或不常用的中级方法,并重命名了剩余的中级方法以提高可用性。 -
使得可以通过静态方法而不是静态字段访问
AttributeMapper
实例Attributes
,以允许延迟初始化并降低启动成本。 -
重塑
Signature.TypeArg
为代数数据类型,以便在类型TypeArg
有界时轻松访问绑定类型。 -
添加了类型感知
ClassReader::readEntryOrNull
和ConstantPool::entryByIndex
方法,如果索引处的条目不是所需类型,则抛出ConstantPoolException
而不是抛出ClassCastException
。这允许类文件处理器指示常量池条目类型不匹配是类文件格式问题,而不是处理器问题。 -
改进了
ClassSignature
类,以便更准确地模拟超类和超接口的通用签名。 -
修复了命名不一致的问题
TypeKind
。 -
从 中删除了实现方法
ClassReader
。
// 创建一个 ClassFile 对象,这是操作类文件的入口。
ClassFile cf = ClassFile.of();
// 解析字节数组为 ClassModel
ClassModel classModel = cf.parse(bytes);
// 构建新的类文件,移除以 "debug" 开头的所有方法
byte[] newBytes = cf.build(classModel.thisClass().asSymbol(),
classBuilder -> {
// 遍历所有类元素
for (ClassElement ce : classModel) {
// 判断是否为方法 且 方法名以 "debug" 开头
if (!(ce instanceof MethodModel mm
&& mm.methodName().stringValue().startsWith("debug"))) {
// 添加到新的类文件中
classBuilder.with(ce);
}
}
});
JEP467: Markdown 文档注释
原文地址:JEP 467: Markdown Documentation Comments
允许用 Markdown 编写 JavaDoc 文档注释,而不是仅仅用 HTML 和 JavaDoc@
标签的混合来编写。
- 通过在文档注释中引入使用 Markdown 语法以及 HTML 元素和 JavaDoc 标签的功能,使 API 文档注释更易于编写,并且更易于以源形式阅读。
- 不会对现有文档注释的解释产生不利影响。
- 扩展Compiler Tree API以允许其他分析文档注释的工具处理这些注释中的 Markdown 内容。
/**
* 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}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code 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.
* <li>If two objects are equal according to the {@link
* #equals(Object) equals} method, then calling the {@code
* hashCode} method on each of the two objects must produce the
* same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link #equals(Object) equals} method, then
* calling the {@code 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.
* </ul>
*
* @implSpec
* As far as is reasonably practical, the {@code hashCode} method defined
* by class {@code 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
*/
//-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
JEP469: Vector API(第八个孵化器)
原文地址:JEP 469: Vector API (Eighth Incubator)
引入一个 API 来表达矢量计算,该计算在运行时可靠地编译为受支持的 CPU 架构上的最佳矢量指令,从而实现优于等效标量计算的性能。
- 清晰简洁的 API — API 应能够清晰简洁地表达各种向量计算,这些计算由循环内组成的向量操作序列组成,并可能带有控制流。应能够表达与向量大小或每个向量的通道数相关的通用计算,从而使此类计算能够在支持不同向量大小的硬件之间移植。
- 平台无关性— API 应与 CPU 架构无关,从而能够在支持矢量指令的多种架构上实现。与 Java API 中常见的情况一样,如果平台优化和可移植性发生冲突,那么我们将倾向于使 API 可移植,即使这会导致某些平台特定的习语无法在可移植代码中表达。
- x64 和 AArch64 架构上可靠的运行时编译和性能— 在功能强大的 x64 架构上,Java 运行时(特别是 HotSpot C2 编译器)应将矢量操作编译为相应的高效且性能卓越的矢量指令,例如 流 SIMD 扩展(SSE) 和高级矢量扩展(AVX) 支持的指令。开发人员应该确信他们表达的矢量操作将可靠地紧密映射到相关的矢量指令。在功能强大的 ARM AArch64 架构上,C2 会将矢量操作编译为NEON和 [REV] 支持的矢量指令。
- 优雅降级— 有时,向量计算无法在运行时完全表达为向量指令序列,这可能是因为架构不支持某些必需的指令。在这种情况下,Vector API 实现应该优雅降级并仍然正常运行。如果向量计算无法有效地编译为向量指令,则可能涉及发出警告。在没有向量的平台上,优雅降级将产生与手动展开循环相媲美的代码,其中展开因子是所选向量中的通道数。
- 与 Project Valhalla 保持一致— Vector API 的长期目标是利用Project Valhalla对 Java 对象模型的增强功能。这主要意味着将 Vector API 当前 基于值的类更改为值类,以便程序可以使用值对象(即缺乏对象标识的类实例)。有关更多详细信息,请参阅有关运行时编译和 未来工作的部分。
以下是对数组元素的简单标量计算:
void scalarComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
以下是使用 Vector API 的等效向量计算:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) {
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
JEP 473: 流收集者 (第二版预览)
原文地址:JEP 473: Stream Gatherers (Second Preview)
增强Stream API以支持自定义中间操作。这将允许流管道以现有内置中间操作无法轻易实现的方式转换数据。
- 使流水线更加灵活和富有表现力。
- 尽可能地允许自定义中间操作来操纵无限大小的流。
流管道由三部分组成:元素源、任意数量的中间操作和终端操作。例如:
long numberOfWords =
Stream.of("the", "", "fox", "jumps", "over", "the", "", "dog") // (1)
.filter(Predicate.not(String::isEmpty)) // (2)
.collect(Collectors.counting()); // (3)
在此示例中,
行 (1) 创建了一个流,但并未求值;
行 (2) 设置了一个中间filter操作,但仍然未求值该流;最后,
行 (3) 上的终止collect操作求值整个流管道。
JEP471:弃用 sun.misc.Unsafe 中的内存访问方法并将其删除
原文地址:JEP 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal
弃用 中的内存访问方法,sun.misc.Unsafe以便在未来的版本中将其删除。这些不受支持的方法已被标准 API 取代,即 VarHandle API(JEP 193,JDK 9)和 Foreign Function & Memory API(JEP 454,JDK 22)。我们强烈建议库开发人员从 迁移到sun.misc.Unsafe受支持的替代方案,以便应用程序可以顺利迁移到现代 JDK 版本。
- sun.misc.Unsafe为未来 JDK 版本中删除内存访问方法做好生态系统的准备。
- 帮助开发人员了解他们的应用程序何时直接或间接依赖于中的内存访问方法sun.misc.Unsafe。
JEP474:ZGC:默认采用分代模式
原文地址:JEP 474: ZGC: Generational Mode by Default
将 Z 垃圾收集器 (ZGC) 的默认模式切换为分代模式。弃用非分代模式,并计划在未来版本中将其删除。
-
表明未来发展将专注于 Generational ZGC 的意图。
-
降低支持两种不同模式的维护成本。
ZGenerational
通过将该选项的默认值从false
改为,使 Generational ZGC 成为 ZGC 的默认模式true
。通过弃用 选项来弃用非分代模式ZGenerational
。
进行这些更改后,将根据提供的命令行参数观察到以下行为:
-
-XX:+UseZGC
- 使用分代 ZGC。
-
-XX:+UseZGC -XX:+ZGenerational
- 使用分代 ZGC。
ZGenerational
发出该选项已被弃用的警告。
-
-XX:+UseZGC -XX:-ZGenerational
- 使用非分代 ZGC。
ZGenerational
发出该选项已被弃用的警告。- 发出警告,指出非代际模式已被弃用并应被删除。
切换到 Generational ZGC 的工作负载可能会遇到日志输出以及可服务性和管理 API 提供的数据方面的差异。
JEP476:模块导入声明(预览)
原文地址:JEP 476: Module Import Declarations (Preview)
通过简洁地导入模块导出的所有包来增强 Java 编程语言。这简化了模块库的重用,但不需要导入代码位于模块本身中。
-
通过允许一次导入整个模块来简化模块库的重用。
-
import com.foo.bar.*
当使用模块导出的 API 的不同部分时,避免多个按需类型导入声明(例如)的噪音 。 -
让初学者更容易使用第三方库和基本 Java 类,而无需了解它们在包层次结构中的位置。
-
不需要使用模块导入功能的开发人员模块化自己的代码。
这是一个预览语言功能,默认情况下禁用
要在 JDK 23 中尝试以下示例,您必须启用预览功能:
- 使用 编译程序javac --release 23 --enable-preview Main.java并使用 运行它java --enable-preview Main;或者,
- 使用源代码启动器时,使用 运行程序java --enable-preview Main.java;或者,
- 使用时jshell,以 启动 jshell --enable-preview。
// exports java.util, which has a public List interface
import module java.base;
String[] fruits = new String[] { "apple", "berry", "citrus" };
Map<String, String> m =
Stream.of(fruits)
.collect(Collectors.toMap(s -> s.toUpperCase().substring(0,1),
Function.identity()));
JEP477:隐式声明的类和实例主要方法(第三次预览)
原文地址:JEP 477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
改进 Java 编程语言,让初学者无需了解为大型程序设计的语言功能即可编写他们的第一个程序。初学者无需使用单独的语言方言,而是可以为单类程序编写精简的声明,然后随着技能的增长无缝扩展他们的程序以使用更高级的功能。经验丰富的开发人员同样可以享受简洁地编写小型程序的乐趣,而无需为大型编程而设计的结构。这是 预览语言功能。
添加两项内容:
-
隐式声明的类会自动导入三种
static
方法,用于通过控制台进行简单的文本 I/O。这些方法在新的顶级类中声明java.io.IO。 -
隐式声明的类会根据需要自动导入模块导出的包的所有公共顶级类和接口
java.base
。
没有使用该特性之前定义一个 main
方法:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
使用该新特性之后定义一个 main
方法:
class HelloWorld {
void main() {
System.out.println("Hello, World!");
}
}
进一步简化(未命名的类允许我们省略类名)
void main() {
System.out.println("Hello, World!");
}
JEP480:结构化并发(第三次预览)
原文地址:JEP 480: Structured Concurrency (Third Preview)
通过引入结构化并发 API 来简化并发编程。结构化并发将在不同线程中运行的相关任务组视为单个工作单元,从而简化错误处理和取消、提高可靠性并增强可观察性。这是一个预览 API。
结构化并发由JEP 428提出,并在JDK 19中作为孵化 API交付。它由JEP 437在JDK 20中重新孵化,并对继承范围值进行了小幅更新(JEP 429)。它首先通过JEP 453在JDK 21中预览,更改为返回 a而不是 a 。它通过JEP 462在JDK 22中重新预览,没有任何变化。我们在此建议在 JDK 23 中再次重新预览 API,没有任何变化,以便获得更多反馈。StructuredTaskScope::fork(...)SubtaskFuture
- 我们的目的并不是替换java.util.concurrent包中的任何并发构造,例如ExecutorService和Future。
- 我们的目标并不是为 Java 平台定义最终的结构化并发 API。其他结构化并发构造可以由第三方库或未来的 JDK 版本定义。
- 定义一种在线程间共享数据流的方法(即通道)并不是我们的目标。我们可能会在将来提出这样做。
- 我们的目标并不是用新的线程取消机制来取代现有的线程中断机制。我们可能会在将来提出这样的建议。
StructuredTaskScope 是一个预览 API,默认情况下处于禁用状态,要使用该StructuredTaskScopeAPI,您必须启用预览 API,如下所示:
- 使用 编译程序javac --release 21 --enable-preview Main.java并使用 运行它java --enable-preview Main;或者,
- 使用源代码启动器时,使用 运行程序java --source 21 --enable-preview Main.java;或者,
- 使用jshell时,使用 启动它jshell --enable-preview。
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<String> user = scope.fork(() -> findUser());
Supplier<Integer> order = scope.fork(() -> fetchOrder());
//合并两个子任务
scope.join()
// ... 并传播错误
.throwIfFailed();
//在这里,两个子任务都已成功,因此请组合它们的结果
return new Response(user.get(), order.get());
}
}
JEP481:范围值(第三次预览)
原文地址:JEP 481: Scoped Values (Third Preview)
引入范围值,使方法能够与线程内的调用方以及子线程共享不可变数据。范围值比线程局部变量更容易推理。它们还具有较低的空间和时间成本,尤其是与虚拟线程 ( JEP 444 ) 和结构化并发 ( JEP 480 ) 一起使用时。这是一个预览 API。
范围值 API 通过JEP 429在 JDK 20 中孵化,通过JEP 446成为 JDK 21 中的预览 API ,并通过JEP 464在 JDK 22 中重新预览。
改变 Java 编程语言并不是我们的目标。我们的目标并不是要求迁移出线程局部变量或者弃用现有的ThreadLocalAPI。
作用域值允许在大型程序中的组件之间安全有效地共享数据,而无需求助于方法参数。
final static ScopedValue<...> NAME = ScopedValue.newInstance();
//在某种方法中
ScopedValue.runWhere(NAME, <value>,
() -> { ... NAME.get() ... call methods ... });
//在直接或间接从lambda表达式调用的方法中
... NAME.get() ..
JEP482:灵活的构造函数主体(第二次预览)
原文地址:JEP 482: Flexible Constructor Bodies (Second Preview)
在 Java 编程语言的构造函数中,允许语句出现在显式构造函数调用之前,即super(..)或this(..)。这些语句不能引用正在构造的实例,但可以初始化其字段。在调用另一个构造函数之前初始化字段可以使类在方法被重写时更加可靠。这是预览语言功能。
让开发人员更自由地表达构造函数的行为,从而能够更自然地放置当前必须纳入辅助静态方法、辅助中间构造函数或构造函数参数的逻辑。保留构造函数在类实例化期间按自上而下的顺序运行的现有保证,确保子类构造函数中的代码不会干扰超类的实例化。
class Person {
private final String name;
private int age;
public Person(String name, int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative.");
}
// 在调用父类构造函数之前初始化字段
this.name = name;
this.age = age;
// ... 其他初始化代码
}
}
class Student extends Person {
private final int studentId;
public Employee(String name, int age, int studentId) {
// 在调用父类构造函数之前初始化字段
this.studentId = studentId;
// 调用父类构造函数
super(name, age);
// ... 其他初始化代码
}
}
JDK23下载地址
Java Downloads | Oracle