Jdk8 17的重要新特性学习
本文档的主要目的是学习从jdk8升级至jdk17的所有重要特性。
单文件的源代码启动
java recordTest.java
这样可以不用生成class文件,只是必须写在一个java文件中。
对于 Java 初学者并希望尝试简单程序的人特别有用,并且能和 jshell 一起使用。一定能程度上增强了使用 Java 来写脚本程序的能力。
模块化系统
解释
引用: Java 9 揭秘(2. 模块化系统) - 林本托 - 博客园
作用: 对包结构进行类型声明
过往的问题: 1, 依赖版本问题: jar依赖之间的相互引用,仅仅是根据类的全限定路径。但是众所周知,java中的大部分的依赖错误都并非是由于找不到全限定路径,而是引入了错误的版本。 核心: 不同版本的依赖(也就是jar)他们具有相同的权限类限定符的类,但是我们无法确定究竟要不要使用。
2,依赖缺失问题: 只有在运行时需要加载某个类时,才会具体的检查他们是否存在。
此外引申的问题是,单体jre(他必须加载完整的类)相比于jdk中裁剪的jre启动速度更慢,消耗内存更多,但是注意,这并不意味着单体jre比jdk更大,只是相比于jdk中jre大,这使得原本是想要通过剥离出一个可运行的jre来减少内存消耗,反倒比安装jdk消耗更大了。
问题解决:
1,显示声明模块的依赖: 模块必须声明对其他模块的显式依赖。 模块系统验证应用程序开发的所有阶段的依赖关系 —— 编译时,链接时和运行时。 假设一个模块声明对另一个模块的依赖,并且第二个模块在启动时丢失。 并报错启动时错误,在这之间会报错运行时操作,且显示的声明对于依赖错误的排除非常有用。 2,模块的强封装 模块必须明确声明其中哪些公共类型可以被其他模块访问。 除非这些模块明确地使其公共类型可访问,否则模块不能访问另一个模块中的公共类型。
改变: 模块化将jdk重新划分为多个模块,这在idea中可以清晰的看到。 ![[Pasted image 20250630160727.png]]
使用
1,模块的可访问性配置:
模块明确地使其公共类型可供其他模块使用,并且使用这些公共类型的模块明确声明对模块的依赖。 模块中的所有未导出的软件包都是模块的私有的,它们不能在模块之外使用。
2,模块图
应用程序的模块化结构可以被视为一个称为模块图。 在模块图中,每个模块都表示为一个节点。 如果第一个模块依赖于第二个模块,则存在从模块到另一个模块的有向边。 通过将称为根模块的一组初始模块的依赖关系与称为可观察模块的模块系统已知的一组模块相结合来构建模块图。
-p 依赖模块位置 -m代表根模块定义/所检查模块 : 模块名:主类
java -p lib -m claim/pkg3.Main
3, 语法
基本语句
五种基本语句
- 导出语句(exports statement): 声明导出模块中的某些包
- 开放语句(opens statement):声明一个开放的模块, 可以使用反射访问
- 需要语句(requires statement): 声明模块对另一个模块的依赖关系
- 使用语句(uses statement):表达服务消费
- 提供语句(provides statement): 表达服务提供
模块命名
受限关键字: 只有当具体位置出现在模块声明中时,它们才具有特殊意义。 可以将它们用作程序中其他地方的标识符。
在JDK 9中, open, module, requires, transitive, exports, opens, to, uses, provides 和 with是受限关键字。
换句话说,可以使用这些受限关键字作为模块名称,例如
// Declare a module named
module module module {
// Module statements go here
}
访问控制
访问控制的语句
导出语句(exports statement): 声明导出模块中的某些包
开放语句(opens statement): 声明一个开放的模块,可以使用反射访问
ps: 开发语句只是表示可以通过反射访问,但是他们不能够直接访问
依赖控制
- transitive: 隐式依赖, 假设p 依赖q,q依赖r, 当我在p依赖q时添加这个关键字时,则p隐式r
- static : 在编译时的依赖是强制的
requires transitive <module>;
requires static <module>;
requires transitive static <module>;
对spi机制的支持
- 使用语句(uses statement):表达服务消费
- 提供语句(provides statement): 表达服务提供
服务消费者语句定义需要使用的接口,服务提供者则定义对服务的支持/实现(可定义多个)。
uses <service-interface>;
provides <service-interface>
with <service-impl-class1>, <service-impl-class2>...;
module org {
opens org.example;
exports org.example;
provides user with userImpl;
}
module test {
requires org;
uses user;
}
使用
ServiceLoader<user> loader = ServiceLoader.load(user.class);
loader.iterator().forEachRemaining(user::show);
观察模块信息的命令
java --module-path ./jdk8-jdk17-1.0-SNAPSHOT.jar --list-modules
ps: 关于命令的含义可以使用java -help查看
4 ,注意事项
1, 确保module-info.java被编译,在target目录下查看 2,无论是导入的项目,还是被依赖的项目,如果想要使用模块配置,都必须module-info.java,否则无法被标识为一个模块。 3,服务提供者和消费者在使用时需要通过反射,那么很显然,他需要对应接口被声明为开放(opens)