一、语法层面革新:让Java更现代化
1.1 文本块:告别字符串拼接噩梦
文本块功能解决了多行字符串处理的痛点,支持更优雅的SQL、JSON、HTML等文本编写。
// JDK17之前
String sql = "SELECT id, name, age\n" +
"FROM users\n" +
"WHERE status = 'ACTIVE'\n" +
"ORDER BY create_time DESC";
// JDK17文本块
String sql = """
SELECT id, name, age
FROM users
WHERE status = 'ACTIVE'
ORDER BY create_time DESC
""";
新增转义字符:
-
\:行连接符,将两行合并为一行 -
\s:单空格,保留格式化中的空格
String formatted = """
Hello,\s%s!
Welcome to \
Java 17!
""".formatted("World");
// 输出:Hello, World!\nWelcome to Java 17!
1.2 Switch表达式增强:从语句到表达式
Switch不再仅仅是if-else的替代品,现在可以作为表达式直接返回值。
// 1. 多case合并(箭头语法)
String dynasty = switch (poet) {
case "李白", "杜甫", "白居易" -> "唐代";
case "苏轼", "辛弃疾" -> "宋代";
default -> "其他朝代";
};
// 2. 作为表达式返回值
int code = switch (status) {
case "SUCCESS" -> {
logger.info("处理成功");
yield 200; // 使用yield返回值
}
case "ERROR" -> {
logger.error("处理失败");
yield 500;
}
default -> 400;
};
// 3. 类型匹配(结合模式匹配)
Object obj = getObject();
String result = switch (obj) {
case Integer i -> "整数: " + i;
case String s && s.length() > 5 -> "长字符串: " + s;
case String s -> "短字符串: " + s;
case null -> "空对象";
default -> "其他类型";
};
1.3 instanceof模式匹配:消除冗余类型转换
简化类型检查和转换的样板代码,提升代码安全性和可读性。
// JDK17之前
if (obj instanceof String) {
String str = (String) obj;
if (str.startsWith("http")) {
// 处理逻辑
}
}
// JDK17模式匹配
if (obj instanceof String str && str.startsWith("http")) {
// str变量自动可用,无需显式转换
processUrl(str);
}
1.4 var局部变量类型推断:平衡简洁与安全
虽然存在争议,但var在适当场景下能提升代码可读性。
// 适合使用var的场景
var users = new ArrayList<User>(); // 类型明确
var stream = Files.lines(path); // 类型明确
var entry = map.entrySet().iterator().next();
// 不适合的场景
var result = process(); // 类型不明确,降低可读性
var count = 10; // 基本类型,没有必要
最佳实践:
-
变量名应具有描述性,弥补类型信息缺失
-
避免在复杂泛型场景过度使用
-
优先保持代码可读性
二、类与封装增强:更安全的面向对象设计
2.1 记录类(Record):简化数据传输对象
Record类是不可变的数据载体,自动实现equals()、hashCode()、toString()等方法。
// 定义Record
public record UserRecord(Long id, String name, String email) {
// 可添加简洁的验证
public UserRecord {
if (id == null || id <= 0) {
throw new IllegalArgumentException("无效的ID");
}
}
// 可添加计算属性
public String displayName() {
return name + " <" + email + ">";
}
}
// 使用
UserRecord user = new UserRecord(1L, "张三", "zhangsan@example.com");
System.out.println(user.id()); // 访问器方法:属性名() 而非 getXxx()
System.out.println(user.toString()); // 自动生成:UserRecord[id=1, name=张三, email=zhangsan@example.com]
// Record不可变
// user.id(2L); // 编译错误,没有setter
Record字节码结构:
类结构: - private final 字段 - 全参构造函数 - 自动生成的访问器(与字段同名) - equals(), hashCode(), toString() - 所有方法标记为final
2.2 隐藏类(Hidden Class):动态代码执行的新范式
隐藏类为框架开发者提供强大的动态类创建能力,特别适合动态代理、Lambda表达式实现等场景。
// 1. 传统类加载 vs 隐藏类
// 传统方式:ClassLoader -> defineClass -> 常规类
// 隐藏类:直接操作字节码 -> 创建对其他类隐藏的类
// 2. 创建隐藏类示例
public class DynamicClassCreator {
public Object createHiddenClass(byte[] classBytes) throws Throwable {
Class<?> hiddenClass = MethodHandles.lookup()
.defineHiddenClass(classBytes, true,
MethodHandles.Lookup.ClassOption.NESTMATE)
.lookupClass();
// 通过反射使用隐藏类
Constructor<?> constructor = hiddenClass.getDeclaredConstructor();
return constructor.newInstance();
}
}
// 3. 实际应用:Lambda表达式底层实现
// Lambda表达式在运行时通过ASM生成字节码,然后使用隐藏类机制加载
Runnable lambda = () -> System.out.println("Hello");
// 底层相当于创建了一个隐藏类实现Runnable接口
隐藏类的优势:
-
生命周期短:适合Lambda、动态代理等临时类
-
卸载友好:不占用永久代/元空间,GC友好
-
安全隔离:对应用代码不可见,防止意外访问
2.3 密封类(Sealed Class):精细化的继承控制
密封类允许开发者精确控制哪些类可以继承或实现当前类,增强API安全性。
// 1. 定义密封类和许可的子类
public sealed abstract class Shape
permits Circle, Rectangle, Triangle {
public abstract double area();
}
// 2. 子类必须声明为 final, sealed, 或 non-sealed
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public sealed class Rectangle extends Shape
permits Square {
protected final double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
public final class Square extends Rectangle {
public Square(double side) {
super(side, side);
}
}
public non-sealed class Triangle extends Shape {
// 非密封,允许任意子类继承
@Override
public double area() {
return 0; // 实现略
}
}
// 3. 使用模式匹配简化处理
Shape shape = getShape();
double area = switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> t.base() * t.height() / 2;
// 不需要default分支,因为Shape是密封的
};
密封类规则:
-
父类和子类必须在同一个模块中
-
子类必须直接继承父类
-
permits列表必须完整
三、模块化系统:Java架构的革命
3.1 模块化基础概念
**模块(Module)**是包(Package)的上层抽象,包含:
-
一组相关的包和资源
-
模块描述符(module-info.java)
-
依赖声明和API导出控制
JDK8 vs JDK17架构对比: JDK8: JAR文件 -> 类路径(Classpath) -> 类加载器 JDK17: JMOD文件 -> 模块路径(Modulepath) -> 模块系统
3.2 模块声明与配置
// module-info.java 示例
module com.example.myapp {
// 1. 依赖声明
requires java.base; // 隐式依赖,可不写
requires java.sql; // 标准模块依赖
requires static lombok; // 编译时依赖
requires transitive utils; // 传递性依赖
// 2. API导出控制
exports com.example.api; // 完全导出(编译+运行)
exports com.example.internal to com.example.tests; // 限定导出
// 3. 反射访问控制
opens com.example.model; // 允许反射访问
opens com.example.internal to spring.core; // 限定开放
// 4. 服务提供与使用
provides com.example.spi.Service
with com.example.impl.DefaultService;
uses com.example.spi.Service;
}
3.3 模块化构建与运行
# 1. 编译模块
javac --module-path libs -d out \
--module-source-path src \
-m com.example.myapp
# 2. 打包模块化JAR
jar --create --file myapp.jar \
--main-class com.example.Main \
-C out/com.example.myapp .
# 3. 运行模块应用
java --module-path libs:myapp.jar \
-m com.example.myapp/com.example.Main
# 4. 创建定制化JRE
jlink --module-path $JAVA_HOME/jmods:myapp.jar \
--add-modules com.example.myapp \
--output customjre \
--strip-debug \
--compress=2
3.4 类加载器体系调整
JDK8类加载器体系:
BootstrapClassLoader
↓
ExtClassLoader
↓
AppClassLoader
↓
自定义类加载器
JDK17类加载器体系:
BootstrapClassLoader(改为可见)
↓
PlatformClassLoader(替代ExtClassLoader)
↓
AppClassLoader
↓
自定义类加载器
关键变化:
-
PlatformClassLoader替代ExtClassLoader -
类加载器继承关系调整
-
模块化感知的类加载逻辑
四、GC与性能优化
4.1 ZGC正式转正
ZGC从JDK11的实验特性到JDK17的正式特性,已成为低延迟应用的首选。
# 启用ZGC
java -XX:+UseZGC -Xmx16G -Xms16G -jar app.jar
# ZGC调优参数
-XX:ZAllocationSpikeTolerance=5 # 分配尖峰容忍度
-XX:ZCollectionInterval=120 # GC触发间隔(秒)
-XX:ZFragmentationLimit=10 # 碎片化限制
ZGC优势:
-
最大停顿时间 < 10ms
-
停顿时间不随堆大小增长
-
支持16TB超大堆(JDK13+)
4.2 废除CMS,完善G1
# CMS已在JDK14移除,以下参数无效:
-XX:+UseConcMarkSweepGC # 已移除
# G1优化建议
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=4M
-XX:InitiatingHeapOccupancyPercent=45
4.3 其他重要优化
-
默认禁用偏向锁(JDK15开始)
# 如需启用(不推荐) -XX:+UseBiasedLocking -
Socket API重写:更简单、可维护性更好
-
内存分配优化:减少TLAB争用
五、GraalVM:Java的未来
5.1 GraalVM简介
GraalVM是Oracle开发的高性能运行时,支持多语言、AOT编译等特性。
# GraalVM社区版下载
https://www.graalvm.org/downloads/
# 安装native-image工具
gu install native-image
5.2 AOT编译实战
// 1. 编写简单应用
public class HelloNative {
public static void main(String[] args) {
System.out.println("Hello from Native Image!");
}
}
// 2. 编译为原生可执行文件
// javac HelloNative.java
// native-image HelloNative
// 3. 运行原生应用
// ./hellonative
AOT编译优势:
-
启动速度:提升10-100倍
-
内存占用:减少约50%
-
打包体积:更小的容器镜像
-
安全增强:减少攻击面
5.3 Spring Boot Native支持
<!-- Spring Native依赖 -->
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>${spring-native.version}</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
# 构建Spring Boot原生应用
mvn -Pnative native:compile
# 运行原生应用
./target/demo-application
六、迁移指南与最佳实践
6.1 从JDK8升级到JDK17
步骤1:依赖检查
# 使用jdeprscan检查已弃用API
jdeprscan --release 17 myapp.jar
# 使用jdeps分析模块依赖
jdeps --multi-release 17 --print-module-deps myapp.jar
步骤2:编译调整
<!-- Maven配置 -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>17</release>
<compilerArgs>
<arg>--enable-preview</arg> <!-- 如需预览特性 -->
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
步骤3:运行时调整
# 类路径 -> 模块路径
# 之前
java -cp "lib/*:myapp.jar" com.example.Main
# 之后
java --module-path "lib:myapp.jar" -m com.example.myapp/com.example.Main
# 或保持兼容性(未模块化应用)
java --class-path "lib/*:myapp.jar" com.example.Main
6.2 常见问题与解决方案
问题1:反射访问被阻止
// 解决方案1:添加JVM参数
--add-opens java.base/java.lang=ALL-UNNAMED
// 解决方案2:模块配置中开放包
opens com.example.internal to spring.core;
问题2:非法反射访问警告
# 禁用警告(不推荐)
--illegal-access=permit
# 最佳实践:修复代码,消除非法反射
问题3:第三方库不兼容
<!-- 使用多版本JAR -->
<dependency>
<groupId>com.example</groupId>
<artifactId>mylib</artifactId>
<version>2.0</version>
<classifier>jdk17</classifier>
</dependency>
6.3 性能调优建议
# 生产环境推荐配置
# 基于G1(通用场景)
java -XX:+UseG1GC \
-Xms4g -Xmx4g \
-XX:MaxGCPauseMillis=200 \
-XX:ParallelGCThreads=4 \
-XX:ConcGCThreads=2 \
-jar app.jar
# 基于ZGC(低延迟场景)
java -XX:+UseZGC \
-Xms8g -Xmx8g \
-XX:ConcGCThreads=4 \
-XX:MaxGCPauseMillis=10 \
-jar app.jar
# 基于GraalVM Native(云原生)
./app-native \
-XX:MaxRAMPercentage=75 \
-Dspring.profiles.active=prod
七、未来展望:超越JDK17
7.1 JDK21虚拟线程预览
// 虚拟线程(JDK21预览)
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
7.2 项目Loom、Valhalla、Amber
-
Loom:轻量级线程(虚拟线程)
-
Valhalla:值类型和内联类
-
Amber:语言特性改进(模式匹配、记录类等)
7.3 云原生Java趋势
-
容器化优化:更小的基础镜像
-
快速启动:AOT编译普及
-
内存效率:堆外内存管理改进
-
可观测性:更好的监控指标
总结
JDK17不仅是Java版本的一次升级,更是Java生态向现代化、云原生转型的重要里程碑。从语法糖到模块化,从GC优化到GraalVM,JDK17为Java开发者提供了构建高性能、可维护、安全应用的强大工具集。
迁移建议:
-
新项目:直接使用JDK17+Spring Boot 3.x
-
存量项目:制定渐进式迁移计划
-
技术选型:根据场景选择G1/ZGC/GraalVM Native