2

我在 intelliJ 中使用 Java 9:我创建了 4 个模块(所有模块都依赖于 module1):

  1. module1:导出包 pkg1 并将 pkg2 导出到模块 2。
  2. module2:需要 module1 并且能够访问这两个包
  3. module3:需要 module1 并且只能访问 pkg1
  4. module4:没有module-info.class,可以访问module1的两个包

我认为自动模块应该需要所有暴露的打包,而不是那些没有暴露的。

我不明白封装的意义是什么,如果它只适用于显式模块化的 JAR。

我错过了什么?

4

3 回答 3

3

在您的示例中module4无法访问pkg2. 自动模块读取所有其他模块(因为它们不能表达依赖关系),但访问类型的规则没有改变,因此只能访问导出包中的公共类型。

基于您的报价的一些背景:

我认为自动模块应该需要所有暴露的打包,而不是那些没有暴露的。

显式模块(即不是自动模块)需要其他模块。在运行时,这些 requires 指令会变成可读性边缘。但它们并不是这些边的唯一来源:对于自动模块,一条边被添加到每个已解析的模块(即,使其成为可读性图)。

所以当你说“自动模块应该需要所有暴露的包”时,有几个错误,可能在翻译中丢失了,但我仍然想澄清一下。这句话应该改为“自动模块读取所有(已解决的)模块”。

在这个阶段,还没有人谈论可访问性和包。这是下一步,这里的自动模块并不特殊,因此它们无法访问内部 API。

于 2017-11-17T10:07:28.123 回答
2

由于模块系统中的自动模块没有模块描述符(module-info.class),因此没有很好的方法来确定自动模块可能依赖于哪些其他模块。

因此,在解析模块图后,自动模块会读取所有其他命名模块,无论是自动的还是显式的。

因此,在您的情况下,module4作为自动模块能够读取显式模块module1


模块系统中定义的可访问性(我得出的相似性)说明为:如果在不同的模块中定义了两种类型S和,并且是,那么在以下情况下代码可以访问:TTpublicST

  • S的模块读取T的模块,(module4读取module1)和

  • T 的模块导出 T 的 package。(module1导出pckg1到所有需要的模块module1并且pckg2只导出到module2


以这种方式无法访问的跨模块边界引用的类型不可用,就像私有方法或字段不可用一样:任何尝试使用它都会导致编译器报告错误,或者IllegalAccessError抛出Java 虚拟机,或者IllegalAccessException由反射运行时 API 抛出。

因此,即使一个类型被声明为 public,如果它的包没有在其模块的声明中导出,那么它只能被该模块中的代码访问

于 2017-11-17T09:08:02.847 回答
0

为了消除 IntelliJ 逻辑,我从我的模块(1、2 和 4)构建了 3 个 JAR,并开始在命令行中使用 java 来玩弄它们。

当我尝试仅使用 -cp 选项执行代码时,它具有与 module4 能够访问 pkg2 相同的行为 -> 这让我得出结论,如果 IntelliJ 模块中没有 module-info.java 文件,它使用类路径。

这是棘手的部分:我使用了 --list-modules 选项来确保 module4 的自动名称是 module4。然后我执行了类似的操作:

java -p "<module1 path>;<module4 path>" -m module4/<MainClass>

我在 pkg1 (导出的包)的一个类上有一个 ClassNotFoundException。

如果我执行:

java -p "<module1 path>;<module2 path>" -m module2/<MainClass>

然后一切正常。

所以看起来如果我们想使用模块路径,主类必须来自模块 JAR。

看起来,自动 JAR 是为了向后兼容,以便我们能够将它们用作新模块的依赖项。

但是,如果要使用模块路径执行模块,Java 假定我们正在执行的 JAR 是模块化 JAR,否则我们应该继续使用类路径。

换句话说,如果我们使用 -p (modulepath) 选项执行一个未模块化的 JAR,它将不需要路径中的其他模块。

所有这些结论都来自于玩 Java,有人能指出它背后的确切理论部分吗?

于 2017-11-18T06:15:47.493 回答