Java 17 是目前 Java 最主流的 LTS 版本,比例已经超越了 Java 8!现在很多新的 Java 开发框架和类库支持的最低 JDK 版本就是 17(比如 AI 开发框架 LangChain4j)。

正式特性
【实用】Sealed 密封类
在很多 Java 开发者的印象中,一个类要么完全开放继承(任何类都能继承),要么完全禁止继承(final 类)。
// 选择1:完全开放继承
public class Shape {
// 问题:不知道会有哪些子类,难以进行穷举
}
// 选择2:完全禁止继承
public final class Circle {
// 问题:即使在同一个模块内也无法继承
}其实这样是没办法精确控制继承关系的,在设计 API 或领域模型时可能会遇到问题。
Java 17 将 Sealed 密封类转正,让类的继承关系变得更可控和安全。
比如我可以只允许某几个类继承:
public sealed class Shape
permits Circle, Rectangle, Triangle {
// 只允许这三个类继承
}但是,被允许继承的子类必须选择一种继承策略:
1)final:到我为止,不能再继承了
public final class Circle extends Shape {
}2)sealed:我也要控制谁能继承我
public sealed class Triangle extends Shape
permits RightTriangle {
}3)non-sealed:我开放继承,任何人都可以继承我
public non-sealed class Rectangle extends Shape {
}强制声明继承策略是为了 确保设计控制权的完整传递。如果不强制声明,sealed 类精确控制继承的价值就会被破坏,任何人都可以通过继承子类来绕过原始设计的限制。
注意,虽然看起来 non-sealed 打破了这个设计,但这也是设计者的主动选择。如果不需要强制声明,设计者可能会无意中失去控制权。
有了 Sealed 类后,某个接口可能的实现类型就尽在掌握了,可以让 switch 模式匹配变得更加安全:
// 编译器知道所有可能的子类型,可以进行完整性检查
public double calculateArea(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.getRadius() * c.getRadius();
case Rectangle r -> r.getWidth() * r.getHeight();
case Triangle t -> 0.5 * t.getBase() * t.getHeight();
// 编译器确保我们处理了所有情况,无需 default 分支
};
}密封类的实际应用
// 定义 API 响应的类型层次
public sealed interface ApiResponse<T>
permits SuccessResponse, ErrorResponse {
}
public record SuccessResponse<T>(T data, String message) implements ApiResponse<T> {}
public record ErrorResponse<T>(int errorCode, String errorMessage) implements ApiResponse<T> {}
// 处理响应
public <T> void handleResponse(ApiResponse<T> response) {
switch (response) {
case SuccessResponse<T>(var data, var message) -> {
System.out.println("成功: " + message);
processData(data);
}
case ErrorResponse<T>(var code, var error) -> {
System.err.println("错误 " + code + ": " + error);
}
// 不需要 default,编译器确保完整性
}
}【了解】增强的伪随机数生成器
Java 17 引入了全新的随机数生成器 API,提供了更优的性能和更多的算法选择:
// 传统的随机数
Random oldRandom = new Random();
int oldValue = oldRandom.nextInt(100);
// 新的随机数生成器
RandomGenerator generator = RandomGenerator.of("L32X64MixRandom");
int newValue = generator.nextInt(100);新的随机数生成器特性
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
public class RandomExample {
public static void main(String[] args) {
// 列出所有可用的算法
RandomGeneratorFactory.all()
.map(RandomGeneratorFactory::name)
.sorted()
.forEach(System.out::println);
// 使用不同的算法
RandomGenerator xoroshiro = RandomGenerator.of("Xoroshiro128PlusPlus");
RandomGenerator l32x64 = RandomGenerator.of("L32X64MixRandom");
// 生成随机数
System.out.println("Xoroshiro: " + xoroshiro.nextInt(1, 101));
System.out.println("L32X64: " + l32x64.nextInt(1, 101));
// 生成随机流
xoroshiro.ints(10, 1, 101)
.forEach(System.out::println);
// 跳跃功能(某些算法支持)
RandomGenerator splittable = RandomGenerator.of("L64X128MixRandom");
if (splittable instanceof RandomGenerator.SplittableGenerator split) {
RandomGenerator newGen = split.split();
System.out.println("分割后的生成器: " + newGen.nextInt(100));
}
}
}新 API 的优势:
更多算法:支持多种高质量的 PRNG 算法
更好性能:针对现代 CPU 优化
功能丰富:支持跳跃、分割等高级功能
统一接口:所有算法使用相同的 API
【了解】强封装 JDK 内部 API
Java 17 进一步强化了对 JDK 内部 API 的封装,一些之前可以通过反射访问的内部类现在完全不可访问,比如:
sun.misc.Unsafecom.sun.*包下的类jdk.internal.*包下的类
虽然这提高了 JDK 的安全性和稳定性,但可能需要迁移一些依赖内部 API 的老代码。
// 这些内部 API 在 Java 17 中被强封装
// sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); // 不再可用
// 如果确实需要(不推荐),可以通过 JVM 参数开放
// java --add-opens java.base/sun.misc=ALL-UNNAMED MyApp迁移指南
// 替代 sun.misc.Unsafe 的现代方案
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class ModernUnsafeAlternative {
private static final VarHandle ARRAY_HANDLE = MethodHandles.arrayElementVarHandle(int[].class);
public static void main(String[] args) {
int[] array = new int[10];
// 使用 VarHandle 进行原子操作
ARRAY_HANDLE.setVolatile(array, 0, 42);
int value = (int) ARRAY_HANDLE.getVolatile(array, 0);
// CAS 操作
boolean success = ARRAY_HANDLE.compareAndSet(array, 0, 42, 100);
System.out.println("CAS 成功: " + success);
}
}【了解】其他正式特性
恢复始终严格的浮点语义
Java 17 恢复了严格的浮点计算语义,确保跨平台的一致性:
// 浮点计算现在在所有平台上都是一致的
public class StrictFloatExample {
public static void main(String[] args) {
double a = 0.1;
double b = 0.2;
double c = a + b;
// 现在在所有平台上结果都一致
System.out.println(c); // 0.30000000000000004
// 使用 BigDecimal 进行精确计算
BigDecimal bd1 = new BigDecimal("0.1");
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal bd3 = bd1.add(bd2);
System.out.println(bd3); // 0.3
}
}新的 macOS 渲染管道
Java 17 在 macOS 上使用新的渲染管道,提供更好的性能和兼容性:
# 默认使用新的渲染管道
java -Dsun.java2d.metal=true MySwingApp
# 如果遇到问题,可以回退到旧管道
java -Dsun.java2d.metal=false MySwingAppmacOS/AArch64 端口
Java 17 正式支持 Apple Silicon(M1/M2)芯片:
# 在 Apple Silicon Mac 上运行
java -version
# 输出会显示 aarch64 架构
# 性能通常比 x86_64 模拟更好
java -XX:+PrintGCDetails MyApp弃用 Applet API
Java 17 将 Applet API 标记为弃用并计划删除:
// 这些 API 已被弃用
// public class MyApplet extends Applet { }
// public class MyJApplet extends JApplet { }
// 推荐使用现代的 Web 技术替代
// - JavaScript + HTML5
// - WebAssembly
// - 桌面应用程序预览特性
【了解】switch 的模式匹配(预览)
Java 17 引入了 switch 的模式匹配作为预览特性:
// 需要编译器预览模式
// javac --enable-preview --release 17 SwitchPatternExample.java
// java --enable-preview SwitchPatternExample
public String processValue(Object value) {
return switch (value) {
case String s -> "字符串: " + s;
case Integer i -> "整数: " + i;
case Long l -> "长整数: " + l;
case Double d -> "浮点数: " + d;
case null -> "空值";
default -> "未知类型: " + value.getClass().getSimpleName();
};
}
// 结合条件判断
public String processString(Object obj) {
return switch (obj) {
case String s && s.length() > 10 -> "长字符串: " + s;
case String s && s.isEmpty() -> "空字符串";
case String s -> "普通字符串: " + s;
case null -> "空值";
default -> "非字符串";
};
}与密封类结合
public sealed interface Expression
permits Constant, Addition, Multiplication {
}
public record Constant(int value) implements Expression {}
public record Addition(Expression left, Expression right) implements Expression {}
public record Multiplication(Expression left, Expression right) implements Expression {}
// 使用模式匹配计算表达式
public int evaluate(Expression expr) {
return switch (expr) {
case Constant(var value) -> value;
case Addition(var left, var right) -> evaluate(left) + evaluate(right);
case Multiplication(var left, var right) -> evaluate(left) * evaluate(right);
// 不需要 default,因为密封类保证了完整性
};
}孵化器特性
【了解】外部函数和内存 API(孵化器)
Java 17 继续完善外部函数和内存 API:
import jdk.incubator.foreign.*;
public class ForeignAPIExample {
public static void main(String[] args) throws Throwable {
// 查找 C 标准库函数
SymbolLookup stdlib = CLinker.systemLookup();
// 查找 printf 函数
MemoryAddress printfAddr = stdlib.lookup("printf").orElseThrow();
// 创建函数描述符
FunctionDescriptor printfDesc = FunctionDescriptor.of(
CLinker.C_INT, // 返回类型
CLinker.C_POINTER // 参数类型(格式字符串)
);
// 创建方法句柄
MethodHandle printf = CLinker.getInstance()
.downcallHandle(printfAddr, printfDesc);
// 调用 printf
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment formatStr = CLinker.toCString("Hello from Java %d\n", scope);
printf.invoke(formatStr, 2024);
}
}
}【了解】向量 API(第二次孵化器)
Java 17 继续改进向量 API:
import jdk.incubator.vector.*;
public class VectorMath {
public static void vectorAdd(float[] a, float[] b, float[] result) {
VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
int upperBound = SPECIES.loopBound(a.length);
// 向量化循环
for (int i = 0; i < upperBound; i += SPECIES.length()) {
FloatVector va = FloatVector.fromArray(SPECIES, a, i);
FloatVector vb = FloatVector.fromArray(SPECIES, b, i);
FloatVector vc = va.add(vb);
vc.intoArray(result, i);
}
// 处理剩余元素
for (int i = upperBound; i < a.length; i++) {
result[i] = a[i] + b[i];
}
}
public static void main(String[] args) {
float[] a = {1, 2, 3, 4, 5, 6, 7, 8};
float[] b = {8, 7, 6, 5, 4, 3, 2, 1};
float[] result = new float[8];
vectorAdd(a, b, result);
System.out.println(Arrays.toString(result));
// [9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0]
}
}性能和工具改进
【了解】上下文特定的反序列化过滤器
Java 17 增强了反序列化安全性:
import java.io.*;
import java.util.function.BinaryOperator;
public class DeserializationFilterExample {
public static void main(String[] args) {
// 设置全局反序列化过滤器
ObjectInputFilter globalFilter = ObjectInputFilter.Config.createFilter(
"java.lang.String;java.lang.Number;!*" // 只允许 String 和 Number
);
ObjectInputFilter.Config.setSerialFilter(globalFilter);
// 或者为特定的流设置过滤器
try (ObjectInputStream ois = new ObjectInputStream(inputStream)) {
ObjectInputFilter contextFilter = info -> {
Class<?> clazz = info.serialClass();
if (clazz != null) {
// 只允许特定的类
if (clazz == String.class || Number.class.isAssignableFrom(clazz)) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
}
return ObjectInputFilter.Status.UNDECIDED;
};
ois.setObjectInputFilter(contextFilter);
Object obj = ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
}
}【了解】JVM 改进
Java 17 包含了多项 JVM 改进:
并发标记清除收集器完全移除
# CMS 收集器在 Java 17 中完全不可用
# java -XX:+UseConcMarkSweepGC MyApp # 会报错
# 推荐使用的收集器
java -XX:+UseG1GC MyApp # G1
java -XX:+UseZGC MyApp # ZGC
java -XX:+UseShenandoahGC MyApp # Shenandoah实验性 AOT 和 JIT 编译器移除
# 这些实验性编译器已被移除
# java -XX:+UseAOTCompilation MyApp # 不再可用
# java -XX:+EnableJVMCI MyApp # 不再可用总结
Java 17 作为新的 LTS 版本,在稳定性和安全性方面做了大量改进。Sealed 类的正式化为类型安全提供了新的工具,增强的随机数生成器提供了更好的性能和功能。
强封装 JDK 内部 API 虽然可能带来一些迁移成本,但长期来看有利于 Java 生态的健康发展。switch 的模式匹配作为预览特性展现了巨大潜力,为未来的函数式编程奠定了基础。
Java 17 已经成为企业级应用的首选版本,其稳定性和新特性的平衡使其成为从 Java 8/11 升级的理想选择。