JDK17 是继JDK8、JDK11之后最重要的LTS长期支持版本2021年9月正式发布Oracle提供长达8年技术支持是目前企业升级Java版本的首选版本。相较于老旧的JDK8JDK17在语法简洁度、代码安全性、封装扩展性、启动性能、内存模型、虚拟机架构做了全方位升级,下面让我们揭开JDK的神秘面纱。一、语法层面新特性JDK17将JDK14/15/16的预览语法特性全部转正补齐了Java长期以来语法臃肿、代码冗余的短板极大提升编码效率。1.1 文本块 Text BlocksJEP 378 正式转正1.1.1 特性简介传统Java多行字符串需要大量换行符\n、转义双引号\代码可读性极差。文本块通过三个双引号包裹多行文本原生支持换行、缩进、特殊字符无需手动转义。1.1.2 实战代码示例// JDK8 繁琐写法 String jsonOld {\n \name\:\张三\,\n \age\:18\n }; // JDK17 文本块极简写法 String jsonNew { name:张三, age:18 } ;1.1.3 新增专属语法\行尾连接符取消当前换行文本无缝拼接\s空格符精准保留单个空格避免缩进错乱自动智能缩进无需手动对齐。1.1.4 使用场景JSON字符串、SQL语句、HTML、XML、日志模板、接口报文拼接彻底告别字符串拼接混乱问题。1.2 Switch 模式匹配增强JEP 406 转正1.2.1 特性简介传统Switch仅支持常量匹配JDK17强化Switch支持类型匹配、模式匹配、null安全、case条件过滤彻底替代臃肿的if-else层级判断。同时添加了yield关键字提供break与switch返回值的功能。1.2.2 实战代码示例public static String getType(Object obj) { return switch (obj) { case Integer i when i 0 - 正整数; case Integer i when i 0 - 负整数; case String s when s.isBlank() - 空字符串; case String s - 普通字符串 s; case null - 空对象; default - 未知类型; }; }多匹配代码示例:switch (name) { case 李⽩, 杜甫, ⽩居易 - System .out .println (唐代诗⼈); case 苏轼, ⾟弃疾 - System .out .println (宋代诗⼈); default - System .out .println (其他朝代诗⼈); }分支返回值示例:int tmp switch (name) { case 李⽩, 杜甫, ⽩居易 - 1 ; case 苏轼, ⾟弃疾 - 2 ; default - { System .out .println (其他朝代诗⼈); yield 3 ; } };1.2.3 核心优势支持when 条件过滤精准匹配复杂场景原生支持 null 匹配杜绝空指针箭头语法无穿透问题无需break表达式可直接返回结果代码极简。1.3 instanceof 模式匹配增强1.3.1 特性简介JDK17彻底简化类型判断强转流程instanceof 支持直接定义匹配变量无需手动强转同时支持when条件筛选。1.3.2 新旧对比// JDK8 传统写法判断强转两步 if (obj instanceof String) { String str (String) obj; System.out.println(str.length()); } // JDK17 极简写法 if (obj instanceof String str !str.isBlank()) { System.out.println(str.length()); } // 进阶when条件匹配 if (obj instanceof Integer num when num 100) { System.out.println(大数 num); }1.3.3 使用场景多类型参数解析、多态类型判断、接口参数校验、业务分支判断大幅减少冗余强转代码。1.4 var 局部变量类型推导稳定版1.4.1 特性简介JDK17将var局部变量推导彻底稳定编译器自动根据右侧赋值内容推导变量类型无需手动声明复杂泛型类型兼顾简洁性与静态类型安全。1.4.2 代码示例// 传统繁琐写法 MapString, ListUser userMap new HashMap(); // JDK17 var推导写法 var userMap new HashMapString, ListUser(); var list List.of(1,2,3,4);1.4.3 限制规则面试考点仅支持局部变量不支持成员变量、方法参数、返回值定义时必须赋值无法先声明后赋值不能用于null无赋值场景。二、模块化以及类封装体系JDK17核心进阶特性JDK17完善了类层级封装、权限管控、代码架构约束新增Record、隐藏类、密封类、模块系统解决传统Java类继承泛滥、权限失控、架构混乱的问题。2.1 记录类 Record数据载体专用类2.1.1 简介Record是JDK17正式固化的纯数据载体类专门用于DTO、VO、BO、数据返回体编译器自动生成构造器、getter、equals、hashCode、toString无需手写模板代码。被reocrd定义的类代表的是⼀种不可变的常量只能⽤来描述⼀种简单的不可变的数据结构。record记录类的实现原理其实⼤致相当于给每个属性添加了private final声明。这样就不允许修改。另外从字节码也能看到对于record类同时还实现了toStringhashcodeequals⽅法⽽这些⽅法都被声明成了final进⼀步阻⽌应⽤定制record相关的业务逻辑。2.1.2 实战代码// 一行代码定义完整数据类 public record UserRecord(Long id, String name, Integer age) {} // 使用 UserRecord user new UserRecord(1L, 张三, 18); System.out.println(user.name());2.1.3 核心约束默认不可变所有成员为final无法被继承、无法定义普通成员变量可自定义方法、静态方法、校验逻辑。2.1.4 使用场景接口返回实体、数据库查询载体、配置实体、中间件传输对象彻底替代Lombok Data注解。2.2 隐藏类 Hidden Classes2.2.1 特性简介JDK17正式引入隐藏类专门用于框架底层动态生成类隐藏类是⼀种不能被其他类直接使⽤的类。隐藏类不再依赖于类加载器⽽是通过读取⽬标类字节码的⽅式创建⼀个对其他类字节码隐藏的class对象然后通过反射的⽅式创建对象调⽤⽅法。仅运行时内部使用。我们先来⼀个示例理解⼀下什么是隐藏类再来思考隐藏类有什么⽤处。先创建一个类:public class HiddenClass { public String sayHello (String name) { return Hello, name ; } public static void printHello (String name) { System .out .printf ( Hello , %s ! Hello , HiddenClass ! %n, name); } }传统⽅式下要使⽤这个类就需要经过编译然后类加载的整个过程。但是隐藏类机制允许直接从编译后的class字节码⼊⼿直接使⽤这个类。⽐如我们可以使⽤下⾯的⽅法获取class字节数组public void printHiddenClassBytesInBase64(){ //编译后的class ⽂件地址 String classPath /Users/roykingw/DevCode/JDK17Demo/demoModule/target/classes/com/roy/hidden/HiddenClass .class; try { byte[] bytes Files.readAllBytes (Paths.get (classPath)); System .out .println (Base64.getEncoder().encodeToString (bytes)); } catch (IOException e) { e .printStackTrace(); } }这样就可以拿到⼀串编码后的class⽂件的字节码。接下来就可以⽤这个字节码直接⽣成这个类。例如public void testInvokeHiddenClass() throws Throwable { //class⽂件的字节码 String CLASS_INFO yv66vgAAADsANgoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQAQADKClWEgAAAAgMAAkACgEAF21ha2VDb25jYXRXa\ XRoQ29uc3RhbnRzAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsJAAwADQcADgwADwAQAQAQamF2YS9sYW5nL1N5c3\ RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwgAEgEAJUhlbGxvICwgJXMgIQpIZWxsbyAsIEhpZGRlbkNsYXNzICEKJW4KABQAFQc\ AFgwAFwAYAQATamF2YS9pby9QcmludFN0cmVhbQEABnByaW50ZgEAPChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9PYmplY3Q7KUxq\ YXZhL2lvL1ByaW50U3RyZWFtOwcAGgEAImNvbS9jbi9jZm0vaGlkZGVuY2xhc3MvSGlkZGVuQ2xhc3MBAARDb2RlAQAPTGluZU51bWJlclRh\ YmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAJExjb20vY24vY2ZtL2hpZGRlbmNsYXNzL0hpZGRlbkNsYXNzOwEACHNheUhlbGxv\ AQAEbmFtZQEAEkxqYXZhL2xhbmcvU3RyaW5nOwEACnByaW50SGVsbG8BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VGaWxlAQAQ\ SGlkZGVuQ2xhc3MuamF2YQEAEEJvb3RzdHJhcE1ldGhvZHMPBgApCgAqACsHACwMAAkALQEAJGphdmEvbGFuZy9pbnZva2UvU3RyaW5nQ29u\ Y2F0RmFjdG9yeQEAmChMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDtMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5n\ L2ludm9rZS9NZXRob2RUeXBlO0xqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpd\ GU7CAAvAQAISGVsbG8sIAEBAAxJbm5lckNsYXNzZXMHADIBACVqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwBwA0AQAeam\ F2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzAQAGTG9va3VwACEAGQACAAAAAAADAAEABQAGAAEAGwAAAC8AAQABAAAABSq3AAGxAAAAAgAc\ AAAABgABAAAAAwAdAAAADAABAAAABQAeAB8AAAABACAACgABABsAAAA7AAEAAgAAAAcrugAHAACwAAAAAgAcAAAABgABAAAABQAdAAAAFgACA\ AAABwAeAB8AAAAAAAcAIQAiAAEACQAjACQAAQAbAAAAQAAGAAEAAAASsgALEhEEvQACWQMqU7YAE1exAAAAAgAcAAAACgACAAAACAARAAwAHQA\ AAAwAAQAAABIAIQAiAAAAAwAlAAAAAgAmACcAAAAIAAEAKAABAC4AMAAAAAoAAQAxADMANQAZ\ ; byte[] classInBytes Base64.getDecoder().decode (CLASS_INFO); Class? proxy MethodHandles.lookup() .defineHiddenClass (classInBytes , true , MethodHandles .Lookup .ClassOption .NESTMATE) .lookupClass(); // 输出类名 System .out .println (proxy.getName()); // 输出类有哪些函数 for (Method method : proxy.getDeclaredMethods()) { System .out .println (method .getName()); } // 2. 调⽤对应的⽅法 MethodHandle mhPrintHello MethodHandles .lookup().findStatic (proxy , printHello, MethodType .methodType (void .class , String .class)); mhPrintHello .invokeExact (loulan); Object proxyObj proxy.getConstructors()[0 ].newInstance(); MethodHandle mhSayHello MethodHandles .lookup().findVirtual (proxy , sayHello, MethodType.methodType (String .class , String .class)); System .out .println (mhSayHello .invoke (proxyObj , loulan)); }示例很简单看似花⾥胡哨但是其实这将是以后开发框架⾮常重要的⼀个特性。因为这些隐藏类直接操作字节码可以极⼤提⾼Java的动态语⾔能⼒。实际上这也是Java吸收其他语⾔优点的⼀种表现。近年来有很多基于JVM的语⾔都在强调动态语⾔。⽐如ScalaKotlin中⼤量运⽤匿名函数Java⾃⼰的Lambda表达式本质上也是⼀种匿名函数。这些匿名函数在语法层⾯并不需要提前声明只要在运⾏时拿来⽤就可以了。但是在JVM中Java⼀切皆对象这些匿名函数也必须经过类加载的繁琐过程并且类的卸载也⾮常受限制。所以在Spring框架中⼤量的运⽤了ASM这样的直接操作字节码的技术就是为了加快这些动态对象的⽣命周期。但是这些技术⽅案的实现即麻烦⼜低效⽽JDK中引⼊了隐藏类机制就可以作为⽣成动态类的新标准。2.2.2 核心作用框架动态代理、动态字节码生成专用避免动态类被业务代码误用、篡改减少类加载冗余提升框架性能。2.2.3 使用场景Spring、Mybatis、动态代理、Lambda匿名类、字节码增强框架底层优化业务开发基本不直接使用属于框架底层增强。2.3 密封类 Sealed ClassesJEP 409 正式转正密封类是JDK17架构级重磅特性解决传统Java继承完全无限制、架构不可控、滥用继承的痛点。在JDK8中每⼀个类都可以被任意多个⼦类继承并修改其中的内置功能。⽐如JDK8中最重要的类加载双亲委派机制在应⽤当中程序员可以随意挑选⼀个内置的类加载器继承出新的类加载器实现随意打破双亲委派机制。这其实是不太安全的意味着很多内置的⾏为得不到保护。⽽密封类就是⽤来限制每⼀个⽗类可以被哪些⼦类继承或者实现。密封类能够保护⽗类的安全⾏为但是也有⼀些限制。⽗类和指定的⼦类必须在同⼀个显式命名的module下并且⼦类必须直接继承⽗类。2.3.1 简介通过sealed关键字定义密封类/接口强制限定可继承/可实现的子类非指定类禁止继承实现架构级权限管控。2.3.2 代码示例// 父类仅允许Student、Teacher两个类继承 public sealed class Person permits Student, Teacher {} // 子类必须声明 final / sealed / non-sealed public final class Student extends Person {} public non-sealed class Teacher extends Person {}2.3.3 三类子类修饰符final终止继承无子类sealed继续密封限定下级子类non-sealed放开继承恢复传统无限制模式。2.3.4 使用场景架构分层、枚举扩展、业务状态机、统一父类管控、框架基础类约束防止代码滥用继承导致架构混乱。2.4 模块系统Java ModuleJava模块系统从JDK9引入JDK17完成全面稳定优化是Java大型项目架构解耦、权限隔离、瘦身启动的核心能力。2.4.1 模块定义模块是比包package更大的架构单元一个模块包含多个包可独立定义依赖关系、导出权限、访问权限实现强隔离、低耦合、可控依赖。简单来说,JDK8中我们写的Java代码是在⼀个⼀个的package下⾯的模块化在包之上增加了更⾼级别的聚合它包括⼀组密切相关的包和资源以及⼀个新的模块描述符⽂件。简单点说module是java中package包的上⼀层抽象。通过modulejava可以更精确的分配对象的⽣效范围。⽐如在JDK8的安装⽬录下JDK预设的功能是以⼀个⼀个jar包的形式存在的但是在JDK17的安装⽬录下你就看不到那些jar包了取⽽代之的是⼀系列以jmod后缀的⽂件这些就是⼀个⼀个的模块。这些jmod⽂件可以认为是⼀种特殊的jar包。JMOD设计为在编译时间和链接时间使⽤但不在运⾏时间使⽤。也就是说应⽤中可以通过只保留需要⽤的jmod⽂件来定制⾃⼰的JRE。但是这些jmod⽂件不能配合javacp/-m等机制使⽤。2.4.2 使用机制每个模块必须包含module-info.java描述文件核心指令requires依赖其他模块(对于显式声明了module-info.java的模块来说模块名是显⽽易⻅的。但是对于没有声明moduleinfo.java的⾮模块化jar包来说默认就会创建具有jar包名称的模块。⽽这个名称还去掉版本号之后的标准包名。另外从JDK9开始JDK所有的内置基础代码都已经按照模块化进⾏了重组所以要使⽤JDK内置的功能也同样需要通过requires声明所需要的依赖。⽐如如果你要使⽤JDBC功能那么就需要单独引⼊java.sql这个模块)exports导出当前模块包允许外部访问(exports关键字开放的成员在编译和运⾏时都可以访问但是不能⽤反射的⽅式访问。如果想要通过反射的⽅式访问⼀个模块内的成员需要改为使⽤opens关键字声明。声明⽅式和exports⼀样)opens开放反射权限uses/providesSPI服务扩展。2.4.3 如何构建模块项目新建标准Maven/Gradle项目在源码根目录创建module-info.java定义模块名、依赖、导出包编译打包模块独立生效。2.4.4 如何加载模块JDK17 JVM启动时自动识别模块配置通过--module指定启动模块替代传统ClassPath启动实现模块化加载、按需加载。2.4.5 模块优点强隔离包权限可控杜绝非法依赖按需加载减少冗余类加载提升启动速度架构清晰模块职责单一大型项目解耦安全增强禁止外部非法反射访问内部包瘦身JRE可自定义精简JRE减少部署体积。2.4.6 模块缺点小型项目配置繁琐、冗余老旧第三方包兼容问题较多反射、动态代理需要手动开放opens权限传统ClassPath项目迁移成本高。三、JDK17 GraalVM 虚拟机详解性能革命JDK17原生深度适配GraalVM替代传统HotSpot虚拟机实现秒级启动、极低内存、原生镜像编译是云原生、微服务、Serverless的最优Java方案也是目前B站、抖音高赞JDK17性能优化核心考点。3.1 GraalVM 介绍GraalVM 是 Oracle 推出的高性能多语言虚拟机JDK17原生集成Graal编译器支持即时编译JIT性能优化AOT静态编译原生镜像多语言混编Java/JS/Python/Go云原生轻量化部署。核心颠覆将Java从重型虚拟机应用变为轻量原生可执行程序。3.2 GraalVM 如何使用3.2.1 开启JIT编译优化JDK17默认开启Graal JIT编译器只需添加启动参数# 启用GraalVM JIT编译 -XX:UnlockExperimentalVMOptions -XX:UseGraalJIT3.2.2 编译Native原生镜像配合SpringBoot3JDK17可直接将项目编译为系统原生可执行文件无需JVM环境。3.3 GraalVM 优缺点3.3.1 优点启动极速传统Java秒级启动 → Graal毫秒级启动内存极低无堆初始化冗余内存占用降低50%性能更强Graal JIT编译优化优于传统C2编译器无需JRE环境原生镜像可独立运行云原生适配适配容器、Serverless、弹性扩缩容。3.3.2 缺点AOT编译耗时极长打包编译速度慢不支持动态特性动态代理、反射、动态类加载受限第三方框架适配有限部分老旧框架不支持原生编译调试难度大原生镜像无法常规debug。3.4 GraalVM 使用场景云原生微服务容器快速启动、弹性伸缩Serverless函数计算毫秒启动节省计费资源轻量中间件、工具程序轻量化部署高并发低延迟服务JIT编译提升峰值性能客户端桌面程序打包独立exe可执行文件。四、全文总结1、JDK17语法层核心提升文本块、增强Switch、模式匹配instanceof、var类型推导彻底解决Java代码臃肿问题编码效率大幅提升。2、类封装体系升级Record简化数据类、隐藏类优化框架底层、密封类管控继承架构、模块系统实现项目解耦与权限隔离让Java工程更规范、安全。3、GraalVM是JDK17最大性能革新从传统重型JVM转为云原生轻量化虚拟机是未来Java微服务、Serverless的核心趋势。4、JDK17作为长期支持版本是目前企业升级最优版本兼顾稳定性、新特性、性能、云原生适配完全替代老旧JDK8。
JDK17新特性详解
发布时间:2026/6/16 22:04:20
JDK17 是继JDK8、JDK11之后最重要的LTS长期支持版本2021年9月正式发布Oracle提供长达8年技术支持是目前企业升级Java版本的首选版本。相较于老旧的JDK8JDK17在语法简洁度、代码安全性、封装扩展性、启动性能、内存模型、虚拟机架构做了全方位升级,下面让我们揭开JDK的神秘面纱。一、语法层面新特性JDK17将JDK14/15/16的预览语法特性全部转正补齐了Java长期以来语法臃肿、代码冗余的短板极大提升编码效率。1.1 文本块 Text BlocksJEP 378 正式转正1.1.1 特性简介传统Java多行字符串需要大量换行符\n、转义双引号\代码可读性极差。文本块通过三个双引号包裹多行文本原生支持换行、缩进、特殊字符无需手动转义。1.1.2 实战代码示例// JDK8 繁琐写法 String jsonOld {\n \name\:\张三\,\n \age\:18\n }; // JDK17 文本块极简写法 String jsonNew { name:张三, age:18 } ;1.1.3 新增专属语法\行尾连接符取消当前换行文本无缝拼接\s空格符精准保留单个空格避免缩进错乱自动智能缩进无需手动对齐。1.1.4 使用场景JSON字符串、SQL语句、HTML、XML、日志模板、接口报文拼接彻底告别字符串拼接混乱问题。1.2 Switch 模式匹配增强JEP 406 转正1.2.1 特性简介传统Switch仅支持常量匹配JDK17强化Switch支持类型匹配、模式匹配、null安全、case条件过滤彻底替代臃肿的if-else层级判断。同时添加了yield关键字提供break与switch返回值的功能。1.2.2 实战代码示例public static String getType(Object obj) { return switch (obj) { case Integer i when i 0 - 正整数; case Integer i when i 0 - 负整数; case String s when s.isBlank() - 空字符串; case String s - 普通字符串 s; case null - 空对象; default - 未知类型; }; }多匹配代码示例:switch (name) { case 李⽩, 杜甫, ⽩居易 - System .out .println (唐代诗⼈); case 苏轼, ⾟弃疾 - System .out .println (宋代诗⼈); default - System .out .println (其他朝代诗⼈); }分支返回值示例:int tmp switch (name) { case 李⽩, 杜甫, ⽩居易 - 1 ; case 苏轼, ⾟弃疾 - 2 ; default - { System .out .println (其他朝代诗⼈); yield 3 ; } };1.2.3 核心优势支持when 条件过滤精准匹配复杂场景原生支持 null 匹配杜绝空指针箭头语法无穿透问题无需break表达式可直接返回结果代码极简。1.3 instanceof 模式匹配增强1.3.1 特性简介JDK17彻底简化类型判断强转流程instanceof 支持直接定义匹配变量无需手动强转同时支持when条件筛选。1.3.2 新旧对比// JDK8 传统写法判断强转两步 if (obj instanceof String) { String str (String) obj; System.out.println(str.length()); } // JDK17 极简写法 if (obj instanceof String str !str.isBlank()) { System.out.println(str.length()); } // 进阶when条件匹配 if (obj instanceof Integer num when num 100) { System.out.println(大数 num); }1.3.3 使用场景多类型参数解析、多态类型判断、接口参数校验、业务分支判断大幅减少冗余强转代码。1.4 var 局部变量类型推导稳定版1.4.1 特性简介JDK17将var局部变量推导彻底稳定编译器自动根据右侧赋值内容推导变量类型无需手动声明复杂泛型类型兼顾简洁性与静态类型安全。1.4.2 代码示例// 传统繁琐写法 MapString, ListUser userMap new HashMap(); // JDK17 var推导写法 var userMap new HashMapString, ListUser(); var list List.of(1,2,3,4);1.4.3 限制规则面试考点仅支持局部变量不支持成员变量、方法参数、返回值定义时必须赋值无法先声明后赋值不能用于null无赋值场景。二、模块化以及类封装体系JDK17核心进阶特性JDK17完善了类层级封装、权限管控、代码架构约束新增Record、隐藏类、密封类、模块系统解决传统Java类继承泛滥、权限失控、架构混乱的问题。2.1 记录类 Record数据载体专用类2.1.1 简介Record是JDK17正式固化的纯数据载体类专门用于DTO、VO、BO、数据返回体编译器自动生成构造器、getter、equals、hashCode、toString无需手写模板代码。被reocrd定义的类代表的是⼀种不可变的常量只能⽤来描述⼀种简单的不可变的数据结构。record记录类的实现原理其实⼤致相当于给每个属性添加了private final声明。这样就不允许修改。另外从字节码也能看到对于record类同时还实现了toStringhashcodeequals⽅法⽽这些⽅法都被声明成了final进⼀步阻⽌应⽤定制record相关的业务逻辑。2.1.2 实战代码// 一行代码定义完整数据类 public record UserRecord(Long id, String name, Integer age) {} // 使用 UserRecord user new UserRecord(1L, 张三, 18); System.out.println(user.name());2.1.3 核心约束默认不可变所有成员为final无法被继承、无法定义普通成员变量可自定义方法、静态方法、校验逻辑。2.1.4 使用场景接口返回实体、数据库查询载体、配置实体、中间件传输对象彻底替代Lombok Data注解。2.2 隐藏类 Hidden Classes2.2.1 特性简介JDK17正式引入隐藏类专门用于框架底层动态生成类隐藏类是⼀种不能被其他类直接使⽤的类。隐藏类不再依赖于类加载器⽽是通过读取⽬标类字节码的⽅式创建⼀个对其他类字节码隐藏的class对象然后通过反射的⽅式创建对象调⽤⽅法。仅运行时内部使用。我们先来⼀个示例理解⼀下什么是隐藏类再来思考隐藏类有什么⽤处。先创建一个类:public class HiddenClass { public String sayHello (String name) { return Hello, name ; } public static void printHello (String name) { System .out .printf ( Hello , %s ! Hello , HiddenClass ! %n, name); } }传统⽅式下要使⽤这个类就需要经过编译然后类加载的整个过程。但是隐藏类机制允许直接从编译后的class字节码⼊⼿直接使⽤这个类。⽐如我们可以使⽤下⾯的⽅法获取class字节数组public void printHiddenClassBytesInBase64(){ //编译后的class ⽂件地址 String classPath /Users/roykingw/DevCode/JDK17Demo/demoModule/target/classes/com/roy/hidden/HiddenClass .class; try { byte[] bytes Files.readAllBytes (Paths.get (classPath)); System .out .println (Base64.getEncoder().encodeToString (bytes)); } catch (IOException e) { e .printStackTrace(); } }这样就可以拿到⼀串编码后的class⽂件的字节码。接下来就可以⽤这个字节码直接⽣成这个类。例如public void testInvokeHiddenClass() throws Throwable { //class⽂件的字节码 String CLASS_INFO yv66vgAAADsANgoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQAQADKClWEgAAAAgMAAkACgEAF21ha2VDb25jYXRXa\ XRoQ29uc3RhbnRzAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsJAAwADQcADgwADwAQAQAQamF2YS9sYW5nL1N5c3\ RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwgAEgEAJUhlbGxvICwgJXMgIQpIZWxsbyAsIEhpZGRlbkNsYXNzICEKJW4KABQAFQc\ AFgwAFwAYAQATamF2YS9pby9QcmludFN0cmVhbQEABnByaW50ZgEAPChMamF2YS9sYW5nL1N0cmluZztbTGphdmEvbGFuZy9PYmplY3Q7KUxq\ YXZhL2lvL1ByaW50U3RyZWFtOwcAGgEAImNvbS9jbi9jZm0vaGlkZGVuY2xhc3MvSGlkZGVuQ2xhc3MBAARDb2RlAQAPTGluZU51bWJlclRh\ YmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAJExjb20vY24vY2ZtL2hpZGRlbmNsYXNzL0hpZGRlbkNsYXNzOwEACHNheUhlbGxv\ AQAEbmFtZQEAEkxqYXZhL2xhbmcvU3RyaW5nOwEACnByaW50SGVsbG8BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VGaWxlAQAQ\ SGlkZGVuQ2xhc3MuamF2YQEAEEJvb3RzdHJhcE1ldGhvZHMPBgApCgAqACsHACwMAAkALQEAJGphdmEvbGFuZy9pbnZva2UvU3RyaW5nQ29u\ Y2F0RmFjdG9yeQEAmChMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDtMamF2YS9sYW5nL1N0cmluZztMamF2YS9sYW5n\ L2ludm9rZS9NZXRob2RUeXBlO0xqYXZhL2xhbmcvU3RyaW5nO1tMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpd\ GU7CAAvAQAISGVsbG8sIAEBAAxJbm5lckNsYXNzZXMHADIBACVqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwBwA0AQAeam\ F2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzAQAGTG9va3VwACEAGQACAAAAAAADAAEABQAGAAEAGwAAAC8AAQABAAAABSq3AAGxAAAAAgAc\ AAAABgABAAAAAwAdAAAADAABAAAABQAeAB8AAAABACAACgABABsAAAA7AAEAAgAAAAcrugAHAACwAAAAAgAcAAAABgABAAAABQAdAAAAFgACA\ AAABwAeAB8AAAAAAAcAIQAiAAEACQAjACQAAQAbAAAAQAAGAAEAAAASsgALEhEEvQACWQMqU7YAE1exAAAAAgAcAAAACgACAAAACAARAAwAHQA\ AAAwAAQAAABIAIQAiAAAAAwAlAAAAAgAmACcAAAAIAAEAKAABAC4AMAAAAAoAAQAxADMANQAZ\ ; byte[] classInBytes Base64.getDecoder().decode (CLASS_INFO); Class? proxy MethodHandles.lookup() .defineHiddenClass (classInBytes , true , MethodHandles .Lookup .ClassOption .NESTMATE) .lookupClass(); // 输出类名 System .out .println (proxy.getName()); // 输出类有哪些函数 for (Method method : proxy.getDeclaredMethods()) { System .out .println (method .getName()); } // 2. 调⽤对应的⽅法 MethodHandle mhPrintHello MethodHandles .lookup().findStatic (proxy , printHello, MethodType .methodType (void .class , String .class)); mhPrintHello .invokeExact (loulan); Object proxyObj proxy.getConstructors()[0 ].newInstance(); MethodHandle mhSayHello MethodHandles .lookup().findVirtual (proxy , sayHello, MethodType.methodType (String .class , String .class)); System .out .println (mhSayHello .invoke (proxyObj , loulan)); }示例很简单看似花⾥胡哨但是其实这将是以后开发框架⾮常重要的⼀个特性。因为这些隐藏类直接操作字节码可以极⼤提⾼Java的动态语⾔能⼒。实际上这也是Java吸收其他语⾔优点的⼀种表现。近年来有很多基于JVM的语⾔都在强调动态语⾔。⽐如ScalaKotlin中⼤量运⽤匿名函数Java⾃⼰的Lambda表达式本质上也是⼀种匿名函数。这些匿名函数在语法层⾯并不需要提前声明只要在运⾏时拿来⽤就可以了。但是在JVM中Java⼀切皆对象这些匿名函数也必须经过类加载的繁琐过程并且类的卸载也⾮常受限制。所以在Spring框架中⼤量的运⽤了ASM这样的直接操作字节码的技术就是为了加快这些动态对象的⽣命周期。但是这些技术⽅案的实现即麻烦⼜低效⽽JDK中引⼊了隐藏类机制就可以作为⽣成动态类的新标准。2.2.2 核心作用框架动态代理、动态字节码生成专用避免动态类被业务代码误用、篡改减少类加载冗余提升框架性能。2.2.3 使用场景Spring、Mybatis、动态代理、Lambda匿名类、字节码增强框架底层优化业务开发基本不直接使用属于框架底层增强。2.3 密封类 Sealed ClassesJEP 409 正式转正密封类是JDK17架构级重磅特性解决传统Java继承完全无限制、架构不可控、滥用继承的痛点。在JDK8中每⼀个类都可以被任意多个⼦类继承并修改其中的内置功能。⽐如JDK8中最重要的类加载双亲委派机制在应⽤当中程序员可以随意挑选⼀个内置的类加载器继承出新的类加载器实现随意打破双亲委派机制。这其实是不太安全的意味着很多内置的⾏为得不到保护。⽽密封类就是⽤来限制每⼀个⽗类可以被哪些⼦类继承或者实现。密封类能够保护⽗类的安全⾏为但是也有⼀些限制。⽗类和指定的⼦类必须在同⼀个显式命名的module下并且⼦类必须直接继承⽗类。2.3.1 简介通过sealed关键字定义密封类/接口强制限定可继承/可实现的子类非指定类禁止继承实现架构级权限管控。2.3.2 代码示例// 父类仅允许Student、Teacher两个类继承 public sealed class Person permits Student, Teacher {} // 子类必须声明 final / sealed / non-sealed public final class Student extends Person {} public non-sealed class Teacher extends Person {}2.3.3 三类子类修饰符final终止继承无子类sealed继续密封限定下级子类non-sealed放开继承恢复传统无限制模式。2.3.4 使用场景架构分层、枚举扩展、业务状态机、统一父类管控、框架基础类约束防止代码滥用继承导致架构混乱。2.4 模块系统Java ModuleJava模块系统从JDK9引入JDK17完成全面稳定优化是Java大型项目架构解耦、权限隔离、瘦身启动的核心能力。2.4.1 模块定义模块是比包package更大的架构单元一个模块包含多个包可独立定义依赖关系、导出权限、访问权限实现强隔离、低耦合、可控依赖。简单来说,JDK8中我们写的Java代码是在⼀个⼀个的package下⾯的模块化在包之上增加了更⾼级别的聚合它包括⼀组密切相关的包和资源以及⼀个新的模块描述符⽂件。简单点说module是java中package包的上⼀层抽象。通过modulejava可以更精确的分配对象的⽣效范围。⽐如在JDK8的安装⽬录下JDK预设的功能是以⼀个⼀个jar包的形式存在的但是在JDK17的安装⽬录下你就看不到那些jar包了取⽽代之的是⼀系列以jmod后缀的⽂件这些就是⼀个⼀个的模块。这些jmod⽂件可以认为是⼀种特殊的jar包。JMOD设计为在编译时间和链接时间使⽤但不在运⾏时间使⽤。也就是说应⽤中可以通过只保留需要⽤的jmod⽂件来定制⾃⼰的JRE。但是这些jmod⽂件不能配合javacp/-m等机制使⽤。2.4.2 使用机制每个模块必须包含module-info.java描述文件核心指令requires依赖其他模块(对于显式声明了module-info.java的模块来说模块名是显⽽易⻅的。但是对于没有声明moduleinfo.java的⾮模块化jar包来说默认就会创建具有jar包名称的模块。⽽这个名称还去掉版本号之后的标准包名。另外从JDK9开始JDK所有的内置基础代码都已经按照模块化进⾏了重组所以要使⽤JDK内置的功能也同样需要通过requires声明所需要的依赖。⽐如如果你要使⽤JDBC功能那么就需要单独引⼊java.sql这个模块)exports导出当前模块包允许外部访问(exports关键字开放的成员在编译和运⾏时都可以访问但是不能⽤反射的⽅式访问。如果想要通过反射的⽅式访问⼀个模块内的成员需要改为使⽤opens关键字声明。声明⽅式和exports⼀样)opens开放反射权限uses/providesSPI服务扩展。2.4.3 如何构建模块项目新建标准Maven/Gradle项目在源码根目录创建module-info.java定义模块名、依赖、导出包编译打包模块独立生效。2.4.4 如何加载模块JDK17 JVM启动时自动识别模块配置通过--module指定启动模块替代传统ClassPath启动实现模块化加载、按需加载。2.4.5 模块优点强隔离包权限可控杜绝非法依赖按需加载减少冗余类加载提升启动速度架构清晰模块职责单一大型项目解耦安全增强禁止外部非法反射访问内部包瘦身JRE可自定义精简JRE减少部署体积。2.4.6 模块缺点小型项目配置繁琐、冗余老旧第三方包兼容问题较多反射、动态代理需要手动开放opens权限传统ClassPath项目迁移成本高。三、JDK17 GraalVM 虚拟机详解性能革命JDK17原生深度适配GraalVM替代传统HotSpot虚拟机实现秒级启动、极低内存、原生镜像编译是云原生、微服务、Serverless的最优Java方案也是目前B站、抖音高赞JDK17性能优化核心考点。3.1 GraalVM 介绍GraalVM 是 Oracle 推出的高性能多语言虚拟机JDK17原生集成Graal编译器支持即时编译JIT性能优化AOT静态编译原生镜像多语言混编Java/JS/Python/Go云原生轻量化部署。核心颠覆将Java从重型虚拟机应用变为轻量原生可执行程序。3.2 GraalVM 如何使用3.2.1 开启JIT编译优化JDK17默认开启Graal JIT编译器只需添加启动参数# 启用GraalVM JIT编译 -XX:UnlockExperimentalVMOptions -XX:UseGraalJIT3.2.2 编译Native原生镜像配合SpringBoot3JDK17可直接将项目编译为系统原生可执行文件无需JVM环境。3.3 GraalVM 优缺点3.3.1 优点启动极速传统Java秒级启动 → Graal毫秒级启动内存极低无堆初始化冗余内存占用降低50%性能更强Graal JIT编译优化优于传统C2编译器无需JRE环境原生镜像可独立运行云原生适配适配容器、Serverless、弹性扩缩容。3.3.2 缺点AOT编译耗时极长打包编译速度慢不支持动态特性动态代理、反射、动态类加载受限第三方框架适配有限部分老旧框架不支持原生编译调试难度大原生镜像无法常规debug。3.4 GraalVM 使用场景云原生微服务容器快速启动、弹性伸缩Serverless函数计算毫秒启动节省计费资源轻量中间件、工具程序轻量化部署高并发低延迟服务JIT编译提升峰值性能客户端桌面程序打包独立exe可执行文件。四、全文总结1、JDK17语法层核心提升文本块、增强Switch、模式匹配instanceof、var类型推导彻底解决Java代码臃肿问题编码效率大幅提升。2、类封装体系升级Record简化数据类、隐藏类优化框架底层、密封类管控继承架构、模块系统实现项目解耦与权限隔离让Java工程更规范、安全。3、GraalVM是JDK17最大性能革新从传统重型JVM转为云原生轻量化虚拟机是未来Java微服务、Serverless的核心趋势。4、JDK17作为长期支持版本是目前企业升级最优版本兼顾稳定性、新特性、性能、云原生适配完全替代老旧JDK8。