Java 9 引入了模块系统以增强 Java 代码的模块化。模块是对包的一种抽象。这个模块系统也被称为 JPMS(Java Platform Module System)。它通常被称为模块。
模块是什么?
模块是一个自描述的代码和数据集合,具有一个标识它的名称。这是一种新的编程组件,它可以包含特定功能的包、配置、资源等。模块能够限制对其所包含包的访问权限。默认情况下,模块内的包中的代码对外部世界不可见,即使通过反射也不行。从 Java 9 开始,Java 平台本身也是模块化的。如果我们使用 --list-modules
命令列出 Java 模块,它将打印出 Java 支持的各种模块,如下所示:
C:\Users\Mahesh>java --list-modules
java.base@20.0.2
java.compiler@20.0.2
java.datatransfer@20.0.2
java.desktop@20.0.2
java.instrument@20.0.2
java.logging@20.0.2
java.management@20.0.2
java.management.rmi@20.0.2
java.naming@20.0.2
java.net.http@20.0.2
java.prefs@20.0.2
java.rmi@20.0.2
java.scripting@20.0.2
java.se@20.0.2
java.security.jgss@20.0.2
java.security.sasl@20.0.2
java.smartcardio@20.0.2
java.sql@20.0.2
java.sql.rowset@20.0.2
java.transaction.xa@20.0.2
java.xml@20.0.2
java.xml.crypto@20.0.2
jdk.accessibility@20.0.2
jdk.attach@20.0.2
jdk.charsets@20.0.2
jdk.compiler@20.0.2
jdk.crypto.cryptoki@20.0.2
jdk.crypto.ec@20.0.2
jdk.crypto.mscapi@20.0.2
jdk.dynalink@20.0.2
jdk.editpad@20.0.2
jdk.hotspot.agent@20.0.2
jdk.httpserver@20.0.2
jdk.incubator.concurrent@20.0.2
jdk.incubator.vector@20.0.2
jdk.internal.ed@20.0.2
jdk.internal.jvmstat@20.0.2
jdk.internal.le@20.0.2
jdk.internal.opt@20.0.2
jdk.internal.vm.ci@20.0.2
jdk.internal.vm.compiler@20.0.2
jdk.internal.vm.compiler.management@20.0.2
jdk.jartool@20.0.2
jdk.javadoc@20.0.2
jdk.jcmd@20.0.2
jdk.jconsole@20.0.2
jdk.jdeps@20.0.2
jdk.jdi@20.0.2
jdk.jdwp.agent@20.0.2
jdk.jfr@20.0.2
jdk.jlink@20.0.2
jdk.jpackage@20.0.2
jdk.jshell@20.0.2
jdk.jsobject@20.0.2
jdk.jstatd@20.0.2
jdk.localedata@20.0.2
jdk.management@20.0.2
jdk.management.agent@20.0.2
jdk.management.jfr@20.0.2
jdk.naming.dns@20.0.2
jdk.naming.rmi@20.0.2
jdk.net@20.0.2
jdk.nio.mapmode@20.0.2
jdk.random@20.0.2
jdk.sctp@20.0.2
jdk.security.auth@20.0.2
jdk.security.jgss@20.0.2
jdk.unsupported@20.0.2
jdk.unsupported.desktop@20.0.2
jdk.xml.dom@20.0.2
jdk.zipfs@20.0.2
C:\Users\Mahesh>
这里我们可以看到,特定于 JDK 的包位于 JDK 模块中,而特定于库的包则位于 Java 模块中。
Java 模块系统的特性
通过模块组件,在 Java 9 中增加了以下改进:
引入了一个新的可选阶段——链接时间。这一阶段介于编译时间和运行时间之间。在此阶段,可以组装并优化一组模块,从而使用 jlink
工具创建自定义的运行时镜像。
javac
、jlink
和 java
添加了额外选项来指定模块路径,从而进一步定位模块的定义。
JAR 格式更新为模块化的 JAR 文件,在其根目录包含 module-info.class
文件。
引入了 JMOD 格式,一种类似于 JAR 的打包格式,可以包含本地代码和配置文件。
声明模块
为了声明一个模块,我们需要在应用程序的根文件夹中创建一个 module-info.java
文件。这个文件包含了所有的元数据或模块描述。
示例
module-info.java
module com.tutorialspoint.greetings {
}
添加依赖模块
我们可以在模块中声明其他模块的依赖关系。例如,如果我们想要使用 com.tutorialspoint.util
模块,则可以添加如下声明:
示例
module com.tutorialspoint.greetings {
requires com.tutorialspoint.util;
}
添加可选模块
我们可以在模块中使用 static
关键字声明其他模块的可选依赖关系。例如,如果我们想要使用 com.tutorialspoint.logging
模块,则可以添加如下声明:
示例
module com.tutorialspoint.greetings {
requires com.tutorialspoint.util;
requires static com.tutorialspoint.logging;
}
添加传递模块
我们可以在模块中使用 transitive
关键字声明其他模块的传递依赖关系。例如,如果我们想要使用 com.tutorialspoint.base
模块作为 com.tutorialspoint.util
模块的依赖,则可以添加如下声明:
示例
module com.tutorialspoint.greetings {
requires com.tutorialspoint.util;
requires static com.tutorialspoint.logging;
requires transitive com.tutorialspoint.base;
}
这意味着如果一个模块想要使用 com.tutorialspoint.greetings
,那么该模块也需要在其模块声明中添加 com.tutorialspoint.base
模块。
导出公共类
默认情况下,模块的一个包中的任何公共类都不会暴露给外界。为了使用公共类,我们需要导出它,如下面所示:
示例
module com.tutorialspoint.greetings {
requires com.tutorialspoint.util;
requires static com.tutorialspoint.logging;
requires transitive com.tutorialspoint.base;
exports com.tutorialspoint.greetings.HelloWorld;
}
允许反射
默认情况下,模块的一个包中的任何私有成员都无法通过反射访问。为了允许反射检查类或模块,我们需要使用 opens
命令,如下面所示:
示例
module com.tutorialspoint.greetings {
requires com.tutorialspoint.util;
requires static com.tutorialspoint.logging;
requires transitive com.tutorialspoint.base;
exports com.tutorialspoint.greetings.HelloWorld;
opens com.tutorialspoint.greetings.HelloWorld;
}
如果我们需要允许整个模块开放以供反射,我们可以使用 open
命令,如下面所示:
open module com.tutorialspoint.greetings {
requires com.tutorialspoint.util;
requires static com.tutorialspoint.logging;
requires transitive com.tutorialspoint.base;
exports com.tutorialspoint.greetings.HelloWorld;
}
创建和使用 Java 模块
按照以下步骤创建一个模块 com.tutorialspoint.greetings
。
步骤 1
创建一个文件夹 C:\>JAVA\src
。然后创建一个文件夹 com.tutorialspoint.greetings
,其名称与我们要创建的模块名称相同。
步骤 2
在 C:\>JAVA\src\com.tutorialspoint.greetings
文件夹中创建 module-info.java
文件,内容如下:
module com.tutorialspoint.greetings { }
module-info.java
文件是用来创建模块的。在这个步骤中,我们创建了一个名为 com.tutorialspoint.greetings
的模块。按照约定,这个文件应该位于与模块名称相同的文件夹中。
步骤 3
向模块中添加源代码。在 C:\>JAVA\src\com.tutorialspoint.greetings\com\tutorialspoint\greetings
文件夹中创建 Java9Tester.java
,内容如下:
package com.tutorialspoint.greetings;
public class Java9Tester {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
按照约定,模块的源代码应该位于与模块名称相同的目录中。
步骤 4
创建一个文件夹 C:\>JAVA\mods
。然后创建一个文件夹 com.tutorialspoint.greetings
,其名称与我们创建的模块名称相同。现在编译模块到 mods
目录。
C:/ > JAVA > javac -d mods/com.tutorialspoint.greetings
src/com.tutorialspoint.greetings/module-info.java
src/com.tutorialspoint.greetings/com/tutorialspoint/greetings/Java9Tester.java
步骤 5
让我们运行模块以查看结果。运行以下命令:
C:/>JAVA>java --module-path mods -m com.tutorialspoint.greetings/com.tutorialspoint.greetings.Java9Tester
这里 module-path
提供了模块位置 mods
,而 -m
指定了主模块。
它将在控制台上打印出以下输出:
Hello World!