模块  java.base

Package java.lang.module

通过解析和服务绑定来支持模块描述符和创建模块配置的类。

除非另有说明,否则将null参数传递给此包中任何类或接口的构造函数或方法将导致抛出NullPointerException 此外,除非另行指定,否则使用包含null元素的数组或集合调用方法将导致NullPointerException

Module Resolution

分辨率是计算模块如何相互依赖的过程。 该过程在编译时和运行时发生。

决议分为两步。 第一步递归枚举一组根模块的'requires'指令。 如果所有枚举模块都是可观察的,那么第二步计算它们的可读性图。 可读性图表体现了模块如何相互依赖,而模块又控制跨模块边界的访问。

第1步:递归枚举

递归枚举采用一组模块名称,查找每个模块声明,并为每个模块声明递归枚举:

  • 'requires'指令给出的模块名称带有'transitive'修饰符,和

  • 根据主机系统的判断,'requires'指令给出的模块名称没有'transitive'修饰符。

模块声明在一组可观察模块中查找。 该组可观察模块以特定于实现的方式确定。 可观察模块集可以包括具有显式声明的模块(即,具有module-info.java源文件或module-info.class文件)和具有隐式声明的模块(即, automatic modules )。 因为自动模块没有明确的模块声明,所以它没有自己的'requires'指令,尽管它的名称可能由显式模块声明的'requires'指令给出。

名称是该算法的初始输入的根模块集以特定于实现的方式确定。 该组根模块可以包括自动模块。

如果此算法枚举了至少一个自动模块,则必须枚举每个可观察的自动模块,无论它们的任何名称是否由显式模块声明的“requires”指令给出。

如果出现以下任何一种情况,则解决方案失败:

  • 任何根模块都是不可观察的。

  • 名称由带有'transitive'修饰符的'requires'指令赋予的任何模块都是不可观察的。

  • 根据主机系统的判断,任何名称由'requires'指令给出而没有'transitive'修饰符的模块是不可观察的。

  • 此步骤中的算法枚举两次相同的模块名称。 这表示'requires'指令中的一个循环,忽略任何'transitive'修饰符。

否则,解析进入步骤2。

第2步:计算可读性图

'requires'指令(不论'传递')表示一个模块依赖于某个其他模块。 'transitive'修饰符的作用是使其他模块也依赖于其他模块。 如果模块M'需要传递N',那么M不仅依赖于N,而且依赖于M的任何模块也依赖于N.这允许M被重构以使其部分或全部内容可以移动到新的模块N没有破坏具有'requires M'指令的模块。

模块依赖性由可读性图表示。 可读性图是有向图,其顶点是步骤1中枚举的模块,其边表示模块对之间的可读性。 边缘指定如下:

首先,可读性由枚举模块的'requires'指令决定,忽略任何'transitive'修饰符:

  • 对于每个枚举模块A,“需要”B:A“读取”B.

  • 对于每个自动的枚举模块X:X“读取”每个其他枚举模块(它“好像”自动模块对每个其他枚举模块都有'requires'指令)。

其次,增加了可读性以解释'传递'修饰符:

  • 对于“读取”B的每个枚举模块A:

    • 如果B'需要传递'C,那么A“读取”C以及B.这种增加是递归的:因为A“读取”C,如果C'需要传递'D,则A“读取”D以及C和B.

    • 如果B是自动模块,则A“读取”每个其他枚举的自动模块。 (它似乎“自动模块”对每个其他枚举的自动模块都要求“传递”指令)。

最后,每个模块都“读取”自己。

如果可读性图中出现以下任何一种情况,则解析失败:

  • 模块“读取”两个或多个具有相同名称的模块。 这包括模块“读取”另一个与其自身名称相同的情况。

  • 两个或多个模块将具有相同名称的包导出到“读取”两者的模块。 这包括包含包“p”的模块M“读取”另一个将p输出到M的模块的情况。

  • 模块M声明它'使用pS'或'提供pS与...'但是包p既不在模块M中也不由M“读取”的任何模块导出到M.

否则,分辨率成功,分辨率的结果是可读性图。

根模块

编译时的根模块集通常是正在编译的模块集。 在运行时,根模块集通常是为'java'启动器指定的应用程序模块。 在未命名模块中编译代码时,或者在从类路径加载主应用程序类时的运行时,默认的根模块集是特定于实现的。 在JDK中,默认的根模块集包含在升级模块路径上或系统模块中可观察的每个模块,并且无需限定即导出至少一个包。

可观察的模块

在编译时和运行时的可观察模块集是通过搜索几个不同的路径,以及通过搜索内置到环境中的已编译模块来确定的。 搜索顺序如下:

  1. 仅在编译时,编译模块路径。 此路径包含源表单中的模块定义。

  2. 升级模块路径。 此路径包含模块的已编译定义,这些模块将优先于(3)和(4)中存在的任何可升级模块的已编译定义进行观察。 请参阅Java SE Platform以了解哪些标准模块可升级。

  3. 系统模块,它是内置于环境中的编译定义。

  4. 应用程序模块路径。 此路径包含库和应用程序模块的已编译定义。

'requires'指令带有'static'修饰符

'requires'指令使'static'修饰符在运行时表达可选的依赖关系。 如果模块声明它“需要静态M”,则解析不会在可观察模块中搜索M以满足依赖性。 但是,如果在步骤1中递归枚举M,那么枚举的所有模块和“需要静态M”将读取M.

完整性

在编译时,解析可能是部分的,因为编译一组模块可能不需要完整的传递闭包。 最低限度,在编译时构造和验证的可读性图包括正在编译的模块,它们的直接依赖性以及所有隐式声明的依赖性(需要传递)。

在运行时,分辨率是一个附加过程。 步骤1中的递归枚举可以相对于先前的分辨率,以便在由先前(或父)分辨率枚举时,不枚举根模块或在'requires'指令中命名的模块。 因此,作为分辨率结果的可读性图可以具有在步骤1中枚举的模块的顶点,但具有边缘以表示模块读取由先前(或父)分辨率枚举的模块。

从以下版本开始:
9