正式特性
【了解】ZGC 默认分代模式
Java 22 引入了分代 ZGC,但当时你需要通过特殊的 JVM 参数来启用它:
java -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+UseGenerationalZGC MyApp而在 Java 23 中,分代模式成为了 ZGC 的默认行为。
虽然听起来只是个小改动,但这个改变的背后是大量的性能测试和实际应用验证的结果。Oracle 的工程师们发现,分代 ZGC 在绝大多数应用场景中都能带来显著的性能改善,特别是在内存分配密集的应用中,性能提升可能达到数倍之多。
# Java 23 中,ZGC 默认使用分代模式
java -XX:+UseZGC MyApp
# 如果需要使用非分代模式(不推荐)
java -XX:+UseZGC -XX:-UseGenerationalZGC MyApp
# 监控分代 ZGC 的性能
java -XX:+UseZGC -Xlog:gc*:gc.log MyApp分代 ZGC 的性能对比
public class ZGCPerformanceExample {
public static void main(String[] args) {
System.out.println("测试分代 ZGC 性能");
// 模拟大量短期对象分配
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
// 创建短期对象(年轻代)
List<String> tempList = new ArrayList<>();
for (int j = 0; j < 100; j++) {
tempList.add("临时字符串" + j);
}
// 偶尔创建长期对象(老年代)
if (i % 10000 == 0) {
staticList.add("长期字符串" + i);
}
}
long endTime = System.currentTimeMillis();
System.out.println("分配时间: " + (endTime - startTime) + "ms");
System.out.println("长期对象数量: " + staticList.size());
}
private static final List<String> staticList = new ArrayList<>();
}运行测试:
# 使用分代 ZGC(默认)
java -XX:+UseZGC -Xmx4g ZGCPerformanceExample
# 使用非分代 ZGC(对比)
java -XX:+UseZGC -XX:-UseGenerationalZGC -Xmx4g ZGCPerformanceExample分代 ZGC 的优势:
- 更高的分配性能:年轻代分配更快
- 更少的 GC 开销:大部分对象在年轻代就被回收
- 更好的内存利用率:减少内存碎片
- 保持低延迟:继承 ZGC 的低延迟特性
【了解】Markdown 文档注释
Java 23 支持在 JavaDoc 中使用 Markdown 语法:
/**
* # 用户服务类
*
* 这个类提供了用户管理的核心功能,包括:
*
* - **创建用户**:注册新用户账户
* - **查询用户**:根据ID或用户名查找
* - **更新用户**:修改用户信息
* - **删除用户**:注销用户账户
*
* ## 使用示例
*
* ```java
* UserService service = new UserService();
* User user = service.createUser("张三", "zhangsan@example.com");
* ```
*
* > **注意**:所有操作都需要适当的权限验证
*
* @author 鱼皮
* @version 1.0
* @since Java 23
*/
public class UserService {
/**
* ## 创建新用户
*
* 根据提供的信息创建一个新的用户账户。
*
* ### 参数说明
*
* | 参数 | 类型 | 描述 |
* |------|------|------|
* | name | String | 用户姓名,**不能为空** |
* | email | String | 电子邮箱,必须是有效格式 |
*
* ### 返回值
*
* 返回创建成功的 `User` 对象,包含:
* - 自动生成的用户ID
* - 创建时间戳
* - 默认的用户设置
*
* @param name 用户姓名
* @param email 电子邮箱
* @return 新创建的用户对象
* @throws IllegalArgumentException 当参数无效时抛出
*/
public User createUser(String name, String email) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("用户姓名不能为空");
}
if (email == null || !isValidEmail(email)) {
throw new IllegalArgumentException("无效的电子邮箱格式");
}
return new User(generateId(), name, email, Instant.now());
}
/**
* ### 验证邮箱格式
*
* 使用正则表达式验证邮箱格式是否有效:
*
* ```regex
* ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
* ```
*
* @param email 要验证的邮箱地址
* @return `true` 如果格式有效,否则 `false`
*/
private boolean isValidEmail(String email) {
return email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
}
private String generateId() {
return "USER_" + System.currentTimeMillis();
}
}
record User(String id, String name, String email, Instant createdAt) {}生成的 JavaDoc 现在支持:
- 标题:使用
#语法 - 列表:支持有序和无序列表
- 表格:Markdown 表格语法
- 代码块:语法高亮的代码块
- 强调:粗体和斜体文本
- 引用:使用
>的引用块 - 链接:Markdown 链接语法
【了解】弃用 sun.misc.Unsafe 中的内存访问方法
Java 23 将 sun.misc.Unsafe 中的内存访问方法标记为弃用:
// 这些方法在 Java 23 中被弃用
// sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
// unsafe.allocateMemory(1024); // 弃用
// unsafe.freeMemory(address); // 弃用
// unsafe.getInt(address); // 弃用
// 推荐使用现代替代方案
import java.lang.foreign.*;
public class ModernMemoryAccess {
public static void main(String[] args) {
// 使用 Foreign Function & Memory API
try (Arena arena = Arena.ofConfined()) {
// 分配内存
MemorySegment segment = arena.allocate(1024);
// 写入数据
segment.set(ValueLayout.JAVA_INT, 0, 42);
segment.set(ValueLayout.JAVA_INT, 4, 24);
// 读取数据
int value1 = segment.get(ValueLayout.JAVA_INT, 0);
int value2 = segment.get(ValueLayout.JAVA_INT, 4);
System.out.println("值1: " + value1 + ", 值2: " + value2);
} // 内存自动释放
// 或者使用 VarHandle 进行原子操作
int[] array = new int[10];
VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(int[].class);
// 原子操作
arrayHandle.setVolatile(array, 0, 100);
int value = (int) arrayHandle.getVolatile(array, 0);
boolean success = arrayHandle.compareAndSet(array, 0, 100, 200);
System.out.println("原子操作结果: " + success + ", 新值: " + array[0]);
}
}迁移指南:
- 内存分配:使用
Arena.allocate()替代allocateMemory() - 内存访问:使用
MemorySegment替代直接内存操作 - 原子操作:使用
VarHandle替代 Unsafe 的 CAS 操作 - 对象操作:使用反射 API 或方法句柄
预览特性
【了解】模式、instanceof 和 switch 中的原始类型(预览)
Java 23 扩展了模式匹配,支持原始类型:
// 需要启用预览功能
// javac --enable-preview --release 23 PrimitivePatternExample.java
public class PrimitivePatternExample {
public static void main(String[] args) {
processValue(42);
processValue(3.14);
processValue("Hello");
processValue(true);
}
// 在 switch 中使用原始类型模式
public static void processValue(Object value) {
switch (value) {
case int i when i > 0 -> System.out.println("正整数: " + i);
case int i when i == 0 -> System.out.println("零");
case int i -> System.out.println("负整数: " + i);
case double d when d > 1.0 -> System.out.println("大于1的浮点数: " + d);
case double d -> System.out.println("小于等于1的浮点数: " + d);
case boolean b -> System.out.println("布尔值: " + b);
case String s -> System.out.println("字符串: " + s);
default -> System.out.println("其他类型");
}
}
// 在 instanceof 中使用原始类型模式
public static String analyzeNumber(Object obj) {
if (obj instanceof int i && i % 2 == 0) {
return "偶数: " + i;
} else if (obj instanceof int i) {
return "奇数: " + i;
} else if (obj instanceof double d && d == Math.floor(d)) {
return "整数形式的浮点数: " + d;
} else if (obj instanceof double d) {
return "小数: " + d;
}
return "不是数字";
}
// 处理数组
public static void processArray(Object array) {
switch (array) {
case int[] ints when ints.length == 0 ->
System.out.println("空整数数组");
case int[] ints when ints.length == 1 ->
System.out.println("单元素整数数组: " + ints[0]);
case int[] ints ->
System.out.println("整数数组,长度: " + ints.length);
case double[] doubles ->
System.out.println("浮点数组,长度: " + doubles.length);
case String[] strings ->
System.out.println("字符串数组,长度: " + strings.length);
default ->
System.out.println("其他类型的数组或非数组");
}
}
}【了解】类文件 API(第二次预览)
Java 23 继续完善类文件 API:
import java.lang.classfile.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
// 需要启用预览功能
public class ClassFileAPIExample {
public static void main(String[] args) throws Exception {
// 动态生成一个计算器类
byte[] calculatorBytes = generateCalculatorClass();
// 将生成的类文件写入磁盘
java.nio.file.Files.write(
java.nio.file.Paths.get("Calculator.class"),
calculatorBytes
);
// 分析现有的类文件
analyzeClassFile(calculatorBytes);
}
// 生成计算器类
public static byte[] generateCalculatorClass() {
return ClassFile.of().build(
ClassDesc.of("Calculator"),
classBuilder -> {
// 添加 add 方法
classBuilder.withMethod(
"add",
MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int),
ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
methodBuilder -> {
methodBuilder.withCode(codeBuilder -> {
codeBuilder.iload(0) // 加载第一个参数
.iload(1) // 加载第二个参数
.iadd() // 相加
.ireturn(); // 返回结果
});
}
);
// 添加 multiply 方法
classBuilder.withMethod(
"multiply",
MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int),
ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
methodBuilder -> {
methodBuilder.withCode(codeBuilder -> {
codeBuilder.iload(0) // 加载第一个参数
.iload(1) // 加载第二个参数
.imul() // 相乘
.ireturn(); // 返回结果
});
}
);
// 添加 main 方法用于测试
classBuilder.withMethod(
"main",
MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String.arrayType()),
ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
methodBuilder -> {
methodBuilder.withCode(codeBuilder -> {
// System.out.println("5 + 3 = " + Calculator.add(5, 3));
codeBuilder.getstatic(ClassDesc.of("java.lang.System"), "out",
ClassDesc.of("java.io.PrintStream"))
.ldc("计算结果:")
.invokevirtual(ClassDesc.of("java.io.PrintStream"), "println",
MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String))
.return_();
});
}
);
}
);
}
// 分析类文件
public static void analyzeClassFile(byte[] classBytes) throws Exception {
ClassModel classModel = ClassFile.of().parse(classBytes);
System.out.println("类名: " + classModel.thisClass().asInternalName());
System.out.println("访问标志: " + classModel.flags().flagNames());
System.out.println("\n方法列表:");
for (MethodModel method : classModel.methods()) {
System.out.println("- " + method.methodName().stringValue() +
method.methodTypeSymbol().displayDescriptor());
// 分析方法字节码
method.code().ifPresent(codeModel -> {
System.out.println(" 字节码指令数: " + codeModel.elementList().size());
});
}
System.out.println("\n字段列表:");
for (FieldModel field : classModel.fields()) {
System.out.println("- " + field.fieldName().stringValue() +
" : " + field.fieldTypeSymbol().displayDescriptor());
}
}
}【了解】Stream Gatherers(第二次预览)
Java 23 继续完善 Stream Gatherers:
import java.util.stream.Gatherer;
import java.util.stream.Gatherers;
// 需要启用预览功能
public class StreamGatherersExample {
public static void main(String[] args) {
// 数据处理示例
List<String> logs = List.of(
"2024-01-01 10:00:00 INFO 用户登录",
"2024-01-01 10:01:00 ERROR 数据库连接失败",
"2024-01-01 10:02:00 INFO 用户注册",
"2024-01-01 10:03:00 WARN 内存使用过高",
"2024-01-01 10:04:00 ERROR 服务器错误",
"2024-01-01 10:05:00 INFO 用户登出"
);
// 1. 滑动窗口分析
System.out.println("=== 滑动窗口分析 ===");
logs.stream()
.gather(Gatherers.windowSliding(3))
.forEach(window -> {
long errorCount = window.stream()
.filter(log -> log.contains("ERROR"))
.count();
if (errorCount >= 2) {
System.out.println("检测到错误集中: " + window);
}
});
// 2. 按批次处理
System.out.println("\n=== 批次处理 ===");
logs.stream()
.gather(Gatherers.windowFixed(2))
.forEach(batch -> {
System.out.println("处理批次: " + batch.size() + " 条日志");
batch.forEach(log -> System.out.println(" " + log));
});
// 3. 自定义 Gatherer - 错误统计
System.out.println("\n=== 错误统计 ===");
Gatherer<String, ?, ErrorStats> errorStatsGatherer = Gatherer.ofSequential(
ErrorStats::new, // 初始状态
(stats, log, downstream) -> {
stats.total++;
if (log.contains("ERROR")) {
stats.errors++;
} else if (log.contains("WARN")) {
stats.warnings++;
} else if (log.contains("INFO")) {
stats.infos++;
}
return true;
},
(stats, downstream) -> {
downstream.push(stats);
return true;
}
);
ErrorStats stats = logs.stream()
.gather(errorStatsGatherer)
.findFirst()
.orElse(new ErrorStats());
System.out.println("总计: " + stats.total + " 条日志");
System.out.println("错误: " + stats.errors + " 条");
System.out.println("警告: " + stats.warnings + " 条");
System.out.println("信息: " + stats.infos + " 条");
// 4. 自定义 Gatherer - 去重相邻元素
System.out.println("\n=== 去重相邻元素 ===");
List<Integer> numbers = List.of(1, 1, 2, 2, 2, 3, 1, 1, 4, 4);
Gatherer<Integer, ?, Integer> deduplicateAdjacent = Gatherer.ofSequential(
() -> new int[]{Integer.MIN_VALUE}, // 上一个值
(state, element, downstream) -> {
if (state[0] != element) {
state[0] = element;
downstream.push(element);
}
return true;
}
);
List<Integer> deduplicated = numbers.stream()
.gather(deduplicateAdjacent)
.toList();
System.out.println("原始: " + numbers);
System.out.println("去重: " + deduplicated);
}
static class ErrorStats {
int total = 0;
int errors = 0;
int warnings = 0;
int infos = 0;
}
}【了解】模块导入声明(预览)
Java 23 引入了模块导入声明:
// 需要启用预览功能
// 传统的导入方式
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// Java 23 的模块导入(预览)
import module java.base; // 导入整个模块的公开 API
public class ModuleImportExample {
public static void main(String[] args) {
// 现在可以直接使用 java.base 模块中的所有公开类
List<String> list = List.of("a", "b", "c");
Map<String, Integer> map = Map.of("x", 1, "y", 2);
Set<String> set = Set.of("1", "2", "3");
String result = list.stream()
.collect(Collectors.joining(", "));
System.out.println(result);
}
}这个特性简化了大量使用某个模块 API 的场景,但需要谨慎使用以避免命名冲突。
【了解】隐式声明类和实例主方法(第三次预览)
Java 23 继续简化简单程序的编写:
// 最简单的 Hello World(预览)
void main() {
println("Hello, World!");
}
// 或者使用字段
String message = "Hello from Java 23!";
void main() {
println(message);
}
// 可以有多个方法
void main() {
String greeting = createGreeting("Java 23");
println(greeting);
}
String createGreeting(String version) {
return "欢迎使用 " + version + "!";
}这个特性让 Java 更适合:
- 教学:学生可以更容易地开始学习
- 脚本:简单的自动化脚本
- 原型:快速验证想法
孵化器特性
【了解】向量 API(第八次孵化器)
Java 23 继续改进向量 API:
import jdk.incubator.vector.*;
public class VectorMathExample {
private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
public static void main(String[] args) {
// 向量化的数学运算
double[] input = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};
double[] result = new double[input.length];
// 计算 f(x) = x^2 + 2*x + 1
vectorizedPolynomial(input, result);
System.out.println("输入: " + Arrays.toString(input));
System.out.println("结果: " + Arrays.toString(result));
// 向量化的矩阵运算
double[][] matrix1 = {{1, 2}, {3, 4}};
double[][] matrix2 = {{5, 6}, {7, 8}};
double[][] product = matrixMultiply(matrix1, matrix2);
System.out.println("\n矩阵乘法结果:");
for (double[] row : product) {
System.out.println(Arrays.toString(row));
}
}
// 向量化多项式计算
public static void vectorizedPolynomial(double[] input, double[] result) {
int upperBound = SPECIES.loopBound(input.length);
for (int i = 0; i < upperBound; i += SPECIES.length()) {
DoubleVector x = DoubleVector.fromArray(SPECIES, input, i);
// f(x) = x^2 + 2*x + 1
DoubleVector x2 = x.mul(x); // x^2
DoubleVector twoX = x.mul(2.0); // 2*x
DoubleVector result_vec = x2.add(twoX).add(1.0); // x^2 + 2*x + 1
result_vec.intoArray(result, i);
}
// 处理剩余元素
for (int i = upperBound; i < input.length; i++) {
double x = input[i];
result[i] = x * x + 2 * x + 1;
}
}
// 向量化矩阵乘法
public static double[][] matrixMultiply(double[][] a, double[][] b) {
int n = a.length;
int m = b[0].length;
int p = a[0].length;
double[][] result = new double[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j += SPECIES.length()) {
DoubleVector sum = DoubleVector.zero(SPECIES);
for (int k = 0; k < p; k++) {
DoubleVector aVec = DoubleVector.broadcast(SPECIES, a[i][k]);
DoubleVector bVec = DoubleVector.fromArray(SPECIES, b[k], j);
sum = aVec.fma(bVec, sum);
}
sum.intoArray(result[i], j);
}
}
return result;
}
}总结
Java 23 最重要的改进是 ZGC 默认启用分代模式,这为大多数应用带来了显著的性能提升。Markdown 文档注释让 API 文档更加美观和易读,弃用 sun.misc.Unsafe 内存访问方法推动了向现代 API 的迁移。
预览特性中,原始类型的模式匹配扩展了类型安全的边界,Stream Gatherers 为复杂的数据处理提供了强大的工具。模块导入声明简化了大型模块的使用,隐式声明类让简单程序的编写更加便捷。
虽然 Java 23 不是 LTS 版本,但它在性能优化、开发体验和语言表达力方面都有重要进展,为后续版本奠定了坚实基础。