Java 16 是一个重要的功能发布版本,为 JVM 和 Java 语言带来了许多特定的变化。它遵循了从 Java 10 开始引入的 Java 发布节奏,于 2021 年 3 月发布,紧随 Java 15 发布仅六个月之后。
需要注意的是,Java 16 是一个非长期支持(non-LTS)版本。
- JEP 338 - Vector API (孵化器)
Vector API 是一个新引入的孵化器特性,允许开发者显式执行向量操作。这个 API 提供了一种在 Java 中表达向量计算的方式,可以在运行时可靠地编译为支持的 CPU 架构上的最优向量指令。
这个特性主要用于需要高性能计算的应用,如科学计算、机器学习等领域。
示例代码:
import jdk.incubator.vector.*;
public class VectorAPIExample {
public static void main(String[] args) {
int[] a = {1, 2, 3, 4};
int[] b = {5, 6, 7, 8};
int[] c = new int[4];
VectorSpecies<Integer> species = IntVector.SPECIES_128;
IntVector va = IntVector.fromArray(species, a, 0);
IntVector vb = IntVector.fromArray(species, b, 0);
IntVector vc = va.add(vb);
vc.intoArray(c, 0);
for (int i : c) {
System.out.println(i);
}
}
}
这个例子展示了如何使用 Vector API 进行简单的向量加法运算。
2. JEP 347 - 启用 C++14 语言特性
Java 16 允许在 JDK 的 C++ 源代码中使用 C++14 语言特性。这个改进主要影响 JDK 的开发者,而不是普通的 Java 开发者。
-
JEP 357 和 JEP 369 - 从 Mercurial 迁移到 Git/GitHub
OpenJDK 源代码已经从 Mercurial 迁移到了 Git/GitHub。这个变化使得贡献代码到 OpenJDK 项目变得更加容易,因为大多数开发者都更熟悉 Git 和 GitHub。 -
JEP 376 - ZGC: 并发线程栈处理
Z 垃圾收集器(ZGC)得到了改进,将线程栈处理从安全点移动到并发阶段。这个改进减少了 ZGC 的暂停时间,提高了应用程序的响应性。 -
JEP 380 - Unix-Domain 套接字通道
SocketChannel 和 ServerSocketChannel 现在支持 Unix 域套接字。这种套接字类型在同一台机器上的进程间通信时比 TCP/IP 套接字更高效。
示例代码:
import java.io.IOException;
import java.net.StandardProtocolFamily;
import java.net.UnixDomainSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.nio.file.Path;
public class UnixDomainSocketExample {
public static void main(String[] args) throws IOException {
Path socketFile = Path.of("/tmp/test.socket");
UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketFile);
// 服务器
try (ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
serverChannel.bind(address);
System.out.println("Server waiting for connection...");
try (SocketChannel channel = serverChannel.accept()) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
buffer.flip();
System.out.println("Server received: " + new String(buffer.array(), 0, bytesRead));
}
}
// 客户端
try (SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX)) {
channel.connect(address);
channel.write(ByteBuffer.wrap("Hello, Unix Domain Socket!".getBytes()));
}
Files.deleteIfExists(socketFile);
}
}
这个例子展示了如何使用 Unix 域套接字进行进程间通信。
6. JEP 386 - Alpine Linux 端口
JDK 现在可用于 Alpine Linux 和其他使用 musl 实现的 Linux 发行版。这扩展了 Java 的可用性,特别是在容器环境中,因为 Alpine Linux 是一个流行的轻量级 Linux 发行版。
- JEP 387 - 弹性元空间
元空间内存管理得到了改进,可以更快地将未使用的 HotSpot 类元数据或元空间内存返回给操作系统,减少元空间占用,并简化元空间代码。
这个改进主要影响 JVM 的内部实现,对开发者来说是透明的,但可能会导致应用程序的内存使用更加高效。
-
JEP 388 - Windows/AArch64 端口
JDK 现在可以在 AArch64 架构的 Windows 系统上运行,包括 ARM 硬件服务器或基于 ARM 的笔记本电脑。这扩展了 Java 的平台支持范围。 -
JEP 389 - 外部链接器 API (孵化器)
引入了新的 API 来替代 JNI(Java Native Interface),允许 Java 代码调用 C/C++ 代码,反之亦然。这个 API 旨在简化 Java 和本地代码之间的互操作性。
示例代码:
import jdk.incubator.foreign.*;
import static jdk.incubator.foreign.CLinker.*;
public class ForeignLinkerExample {
public static void main(String[] args) {
try (var scope = ResourceScope.newConfinedScope()) {
MemorySegment hello = CLinker.toCString("Hello from Java!", scope);
MemorySegment world = CLinker.toCString("world", scope);
MemorySegment printf = SymbolLookup.loaderLookup().lookup("printf").get();
CLinker.getInstance().downcallHandle(
printf,
MethodType.methodType(int.class, MemoryAddress.class),
FunctionDescriptor.of(C_INT, C_POINTER)
).invokeExact((MemoryAddress) hello);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
这个例子展示了如何使用外部链接器 API 调用 C 语言的 printf 函数。
- JEP 390 - 基于值的类的警告
当使用 synchronized 关键字同步基于值的类时,现在会产生警告。基于值的类是那些应该像原始类型一样被对待的类,例如 java.lang.Integer 或 java.time.LocalDate。
示例代码:
public class ValueBasedClassWarningExample {
public static void main(String[] args) {
Integer i = 42;
synchronized (i) { // 这里会产生警告
System.out.println("This is not recommended");
}
}
}
- JEP 392 - 打包工具
jpackage 工具现在成为了标准特性,不再是孵化器特性。这个工具可以将 Java 应用程序打包成特定平台的安装包。
示例命令:
jpackage --name MyApp --input lib --main-jar myapp.jar --main-class com.example.Main
这个命令将创建一个名为 MyApp 的应用程序安装包。
- JEP 393 - 外部内存访问 API (第三次孵化)
外部内存访问 API 得到了一些小的增强。这个 API 允许 Java 程序安全高效地访问 Java 堆之外的内存。
示例代码:
import jdk.incubator.foreign.*;
public class ForeignMemoryAccessExample {
public static void main(String[] args) {
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(100, scope);
MemoryAccess.setInt(segment, 0, 42);
int value = MemoryAccess.getInt(segment, 0);
System.out.println("Value: " + value);
}
}
}
这个例子展示了如何分配和访问本地内存。
- JEP 394 - instanceof 的模式匹配
instanceof 的模式匹配现在成为了标准特性。这个特性简化了类型检查和类型转换的代码。
示例代码:
public class PatternMatchingExample {
public static void printLength(Object obj) {
if (obj instanceof String s) {
System.out.println(s.length());
} else {
System.out.println("Not a string");
}
}
public static void main(String[] args) {
printLength("Hello");
printLength(42);
}
}
- JEP 395 - Records
记录(Records)现在成为了标准特性。记录是一种简洁的方式来声明不可变的数据类。
示例代码:
public record Person(String name, int age) {}
public class RecordExample {
public static void main(String[] args) {
Person person = new Person("Alice", 30);
System.out.println(person.name());
System.out.println(person.age());
System.out.println(person);
}
}
-
JEP 396 - 默认强封装 JDK 内部元素
–illegal-access 选项的默认模式现在是 “deny”。这意味着默认情况下,强封装 JDK 的内部元素,提高了 Java 程序的安全性和可维护性。 -
JEP 397 - 密封类 (第二次预览)
密封类得到了一些小的增强。密封类允许类的作者精确控制哪些类可以继承它。
示例代码:
public sealed class Shape permits Circle, Rectangle, Square {
// ...
}
final class Circle extends Shape {
// ...
}
final class Rectangle extends Shape {
// ...
}
final class Square extends Shape {
// ...
}
在这个例子中,Shape 类只允许 Circle、Rectangle 和 Square 继承它。
总结:Java 16 带来了许多有趣和有用的新特性,从语言层面的改进(如记录和模式匹配)到底层的优化(如 ZGC 改进和弹性元空间)。虽然它是一个非 LTS 版本,但这些特性为 Java 的未来发展奠定了基础,许多特性在后续的 LTS 版本中都会成为标准。开发者应该关注这些新特性,并在适当的时候在项目中使用它们,以提高代码质量和性能。