0. 實例
如何使用模塊--從編寫源代碼到編譯,打包和運行程序。
0.1 使用命令行編寫和運行模塊程序
0.1.1 設置目錄
使用如下目錄層次結構來編寫,編譯,打包和運行源代碼:

src目錄用於保存源代碼,其中包含一個com.jdk9.m的子目錄,並且創建一個同名的com.jdk9.m模塊名,並將其源代碼保存在整個子目錄下。注:這個子目錄不一定要與模塊名相同。
mods目錄將已編譯的代碼保存在展開的目錄層次結構中。如果需要,可以使用此目錄中的代碼運行應用程序。
lib存儲打包成一個模塊化的JAR,可以使用模塊化JAR來運行程序,也可以將模塊JAR提供給可以運行程序的其他開發人員。
0.1.2 編寫源代碼
創建一個名為module-info.java的文件,在文件中聲明模塊的代碼:
module com.jdk9.m {
}
JDK9中的每個Java類型都是模塊的成員,甚至是int,long和char等原始類型。所有原始類型都是java.base模塊的成員。JDK9中的Class類有一個名為getModule()的新方法,它返回該類作為其成員的模塊引用。
注:所有原始數據類型都是java.base模塊的成員,可以使用int.class.getModule()獲取int基本類型的模塊的引用。
Welcome類的源代碼如下:
package com.jdk9.m
public class Welcome {
public static void main(String[] args) {
Class<Welcome> cls = Welcome.class;
Module mod = cls.getModule();
String modName = mod.getName();
System.out.println("Module Name: " + modName);
}
}
最終的目錄結構如下:

0.1.3 編譯
使用javac命令編譯遠點並將編譯的代碼保存在mods目錄下。
> javac -d mods --module-source-path src src\com.jdk.m\module-info.java src\com.jdk.m\Welcome.java
-d mods將所有編譯的類文件保存到mods目錄下。
--modules-source-path src指定src目錄的子目錄包含多個模塊的源代碼,其中每個子目錄名稱與包含源代碼的子目錄的模塊名稱相同。
可以使用javac的--module-version選項,可以指定正在編譯的模塊的版本,模塊版本保存在module-info.class文件中。

0.1.4 打包模塊代碼
將模塊的編譯代碼打包成一個模塊化的JAR。
> jar --create --file lib\com.jdk.m-1.0.jar --main-class com.jdk.m.Welcome --module-version 1.0 -C mods/com.jdk.m .
--create選項表示要創建一個新的模塊化JAR。
--file選項用於指定新的模塊化JAR的位置和名稱,將新的模塊化JAR保存在lib目錄中。
--main-class指定main方法作為應用程序入口。
--module-version指定模塊版本。
-C指定執行jar命令時將用作設置當前目錄。
0.1.5 運行程序
使用java命令來運行java程序:
語法:java --module-path <module-path> --module <module>/<main-class>
> java --module-path lib --module com.jdk.m/com.jdk.m.Welcome
這里,--module-path用於定位模塊的模塊路徑。
--module指定與其主類一起運行的模塊。
0.2 使用eclipse編寫和運行模塊程序
0.2.1 創建Java工程
通過eclipse的工程創建向導創建一個Java工程,創建完成之后,需要注意,現在JDK9的類已經以模塊化的形式進行管理了,如圖:

0.2.2 目錄結構

human的源碼如下:
package com.jdk9.human;
public class Human {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Module m = Human.class.getModule();
System.out.println(m);
}
}
module-info.java的源碼如下:
module com.jdk9.human {}
0.2.3 運行
運行Human.main,結果如下:

使用模塊開發Java應用程序不會改變Java類型被組織成包的方式,模塊的源代碼在包層次結構的根目錄下包含一個module-info.java文件,也就是說,module-info.java文件放在未命令的包中,它包含模塊聲明。
1. 背景
JDK9最重要和最令人激動的功能之一是模塊系統,該模塊系統是以代碼名稱Jigsaw的項目開發的。
在JDK9之前,開發一個Java應用程序通常包括以下步驟:
- Java源代碼以Java類型(類,接口,枚舉和注釋)的形式編寫。
- 不同的Java類型被安排在一個包中,而且始終屬於一個明確或默認的包。
- 編譯的代碼被打包成一個或多個jar文件,也稱為應用程序jar。因為它們包含應用程序代碼,一個程序包張的代碼可能會引用多個jar。
- 應用程序可能使用類庫,類庫作為一個或多個jar文件提供給應用程序使用。
- 通過將所有jar文件,應用程序jar文件來jar類庫放在類路徑上來部署應用程序。
20多年來,Java社區以這種編寫,編譯,打包和部署Java代碼的方式開發。這樣部署和運行Java代碼存在如下問題:
- 一個包只是一個類型等待容器,而不強制執行任何可訪問性邊界:包中的公共類型可以在所有其他包中訪問;沒有辦法阻止在一個包中公開類型的全局可見性。
- 除了以java和javax開頭的包外,包穎是開放擴展的。如果你在具有包級別訪問的jar中進行類型化,則可以在其他jar中訪問定義與你的名稱相同的包中的類型。
- Java運行時會看到從jar列表加載的一組包。沒有辦法知道是否在不同的jar中有多個相同類型的副本。Java運行時首先加載的類路徑中遇到的jar中找到的類型。
- Java運行時可能會出現由於應用程序在類路徑中需要的其中一個jar引起的運行時缺少類型的情況,當代碼嘗試使用它們時,缺少的類型會引起運行時錯誤。
- 在啟動時沒有辦法知道應用程序中使用的某些類型已經丟失。還可以包含錯誤的jar文件版本,並在運行時產生錯誤。
這些問題在Java社區中非常頻繁,得到一個名字--JAR-Hell。
Java9通過引入開發,打包和部署Java應用程序的新方法來解決這些問題,在Java9中,Java應用程序由稱為模塊的小型交互組件組成,Java9已經將JDK/JRE組織為一組模塊。
2. 全新的模塊系統
Java9引入了一個稱為模塊的新的程序組件,可以將Java程序視為具有明確定義的邊界和這些模塊之間依賴關系的交互模塊的集合。模塊系統的開發具有以下目標:
(1)可靠的配置
(2)強封裝
(3)模塊化JDK/JRE
可靠的配置解決了用於查找類型的容易出錯的類路徑機制的問題,模塊必須聲明對其他模塊的顯示依賴。模塊系統驗證應用程序開發的所有階段的依賴關系--編譯時,鏈接時和運行時。假設一個模塊聲明對另一模塊的依賴,並且第二個模塊在啟動時丟失,JVM檢測到依賴關系丟失,並在啟動時失敗,在Java9之前,當使用缺少的類型時,這樣的應用程序會生成運行時錯誤(不是啟動時)。
強大的封裝解決了類路徑上跨JAR的公共類型的可訪問性問題。模塊必須明確聲明其中哪些公共類型可以被其他模塊訪問。
3. 什么是模塊化
模塊是代碼和數據集合,它可以包含Java代碼和本地代碼。
對於Java代碼,模塊可以看做零個或多個包的集合。除了其名稱,模塊定義包括以下內容:
- requires其他模塊(或依賴於)的列表
- exports包列表(其public API),其他模塊可以使用
- open的包(整個API,共有和私有),其他模塊可以反射調用
- 使用的服務列表
- 提供的服務的實現列表
4. 模塊依賴關系
假設有兩個模塊com.jdk9.human:包含Human類,com.jdk9.address:包含Address類;
其中Human想使用Address類,其模塊圖如下:

在eclipse中,創建兩個名為human和address的Java項目,每個項目都將包含與項目名稱相同的模塊的代碼。
目錄結構如下:

address工程的主要代碼是:
package com.jdk9.address;
public class Address {
private String line = "1111 Main Blvd";
private String city = "Jacksonville";
private String state = "FL";
private String zip = "32256";
public Address() {}
public Address(String line, String city, String state, String zip) {
this.line = line;
this.city = city;
this.state = state;
this.zip = zip;
}
@Override
public String toString() {
return "[line: " + line + ", city: " + city + ", state: " + state + ", zip: " + zip + "]";
}
}
module com.jdk9.address {
exports com.jdk9.address;
}
human工程的主要代碼如下:
package com.jdk9.human;
import com.jdk9.address.Address;
public class Human {
private String name;
private Address address = new Address();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public static void main(String[] args) {
Human human = new Human();
System.out.println(human.getAddress());
}
}
module com.jdk9.human {
requires com.jdk9.address;
}
運行結果如下:

此外,為了能夠在human工程中直接引用address工程的com.jdk9.address模塊,需要做如下設置:
右擊address工程-->Build Path-->Configure Build Path...-->切換到Projects,然后設置模塊路徑關聯,如下圖:

exports語句用於將包導出到所有其他模塊或某些命名模塊,導出的包中的所有公共類型都可以在編譯時和運行時訪問。在運行時,可以使用反射來訪問公共類型的公共成員。即使在這些成員上使用setAccessible(true)方法,公共類型的非公共成員也無法使用反射。 該語句將包中的所有公共類型導出到所有其他模塊。
com.jdk9.address模塊導出com.jdk9.address包,因此Address類可以由其他模塊使用,它是公共的,也可以在com.jdk9.human包中使用。
Human類在com.jdk9.human模塊中,它使用com.jdk9.address模塊中的Address類型中的字段。這意味着com.jdk9.human模塊讀取com.jdk9.address模塊。這通過com.jdk9.human模塊中聲明requires語句。
requires語句用於指定一個模塊對另一個模塊的依賴,requires語法如下:
requires [transitive] [static] <module>;
<module>:是當前模塊讀取的另一個模塊的名稱;
static:則<module>模塊在編譯時是必需的,但在運行時是可選的;
transitive:級聯依賴;
JDK9之前,一個包中的public類型可以被前台包訪問,沒有任何限制。也就是說,包沒有控制它們包含的類型的可訪問性。JDK9中的模塊系統對類型的可訪問性提供了細粒度的控制。
模塊之間的可訪問性是所使用的模塊和使用模塊之間的雙向協議:模塊明確地使其公共類型可供其他模塊使用,而且使用這些公共類型的模塊明確聲明對第一個模塊的依賴,模塊中所有未導出的軟件包都是模塊的私有的,他們不能再模塊之外使用。
將包中的API設置為公共供其他模塊使用被稱之為導出包。
模塊系統只知道一個模塊:java.base,java.base模塊不依賴與任何其他模塊,所有其他模塊都隱含地依賴於java.base模塊。
構建模塊圖旨在編譯時,鏈接時和運行時解析模塊依賴關系,模塊解析從根模塊開始,並遵循依賴關系鏈接,直到達到java.base模塊。
5. 聚合模塊
可以創建一個不包含任何代碼的模塊,它收集並重新導出其他模塊的內容,這樣的模塊稱為聚合模塊。假設有一個模塊依賴於五個模塊,可以為這五個模塊創建一個聚合模塊,現在你的模塊只要依賴於一個模塊--聚合模塊。
6. 聲明模塊
本節包含用於聲明模塊的語法的快速概述。使用模塊聲明來定義模塊,是Java編程語言中的新概念:
[open] module <moduleName> {
<module-statement>;
......
}
open修飾符是可選的,它聲明一個開放的模塊,一個開放的模塊導出所有的包,以便其他模塊使用反射訪問。<moduleName>是要定義的模塊的名稱,<module-statement>是一個模塊語句。模塊聲明中可以包含零個或多個模塊語句:
- 導出語句(exports),導出模塊,其他模塊訪問。
- 開放語句(opens),開放當前模塊,其他模塊可以訪問,包括反射調用等。
- 需要語句(requires),聲明模塊對另一個模塊的依賴關系。
- 使用語句(uses),表達服務消費。
- 提供語句(provides),表達服務提供。
6.1 模塊命名
6.1.1 模塊命名關鍵字
模塊名稱可以是Java限定標識符,與包命名約定類型,使用反向域名模式為模塊提供唯一的名稱。
JDK9中,open,module,requires,transitive,exports,opens,to,uses,provices,with是受限關鍵字,只有在具體位置出現在模塊聲明中時,它們才具有特殊意義。可以將它們用作程序中其他地方的標識符。
6.1.2 模塊命名約束
(1)將包拆分成多個模塊是不允許的,也就是說,同一個包不能在多個模塊中定義;
(2)不能同時訪問多個模塊中的相同軟件包;
(3)模塊圖不能包含循環依賴,也就是說兩個模塊不能彼此讀取,如果需要,他們應該是一個模塊,而不是兩個;
(4)模塊聲明不支持模塊版本,需要使用jar工具或其他一些工具(javac)將模塊的版本添加為類文件屬性;
(4)模塊系統沒有子模塊的概念,com.jdk9.address和com.jdk9.address.child是兩個單獨的模塊。
6.2 模塊的訪問控制
6.2.1 exports
導出語句將模塊的指定包導出到所有模塊或編譯時和運行時的命令模塊列表。形式如下:
exports <package>;
假設需要開發多個模塊組成的庫或框架,其中有一個模塊中的包含API,僅供某些模塊內部使用。也就是說,該模塊中的包不需要導出到所有模塊,而是其可訪問性必須限於幾個命名的模塊,可以使用模塊聲明中的限定的export to語句來實現:
exports <package> to <module1>, <module2>;
package:當前模塊要導出的包的名稱;
module1,module2……:可以讀取當前模塊的模塊的名稱。
實例,包含非限定導出和限定導出:
module com.jdk9.module {
exports com.jdk9.module.core;
exports com.jdk9.module.util to com.jdk9.module.internal, com.jdk9.module.server;
}
6.2.2 opens
Java允許使用反射機制訪問所有成員,包括私有,公共,包和受保護的類型。需要在成員對象上調用setAccessible(true)方法。
模塊系統提供如下規則:
- exports的包將允許在編譯和運行時訪問public類型及其public成員,如果不exports包,則該包中的所有類型都不可訪問其他模塊;
- 可以打開一個模塊,以便在運行時對該模塊中的所有包中的所有類型進行深層反射,這樣的模塊稱為開放模塊;
開放語句允許對所有模塊的反射訪問指定的包或運行時指定的模塊列表。其他模塊可以反射訪問指定包中的所有類型以及這些類型的所有成員(私有和公共),開放語句采用如下形式:
opens <package>;
opens <package> to <module1>, <module2>;
6.2.3 開放模塊
open com.jdk9.address {
exports xxx;
requires xxx;
uses xxx;
provides xxx;
// 不允許opens
}
定義com.jdk9.address模塊是一個開放模塊,其他模塊可以在本模塊中的所有軟件包上對所有類型使用深層反射。可以在開放模塊中聲明exports,requires,uses和provides語句,但不能再opens的模塊中再聲明opens語句。opens語句用於打開特定的包以進行深層反射,因為開放模塊打開所有的軟件包進行深層反射,所以在開放模塊中不允許再使用open語句。
6.2.4 打開包
打開一個包意味着其他模塊對該包中的類型使用深層反射,可以打開一個包指定給所有其他模塊或特定的模塊列表,打開一個包到所有其他模塊的打開語句的語法如下:
opens <package>;
opens <package> to <module1>, <module2>……
<package>僅用於深層反射到<module1>,<module2>等。
6.2.5 訪問類型
在JDK9之前,有4中訪問類型:
- public
- protected
- <package>
- private
在JDK8中,public類型意味着程序的所有部分都可以訪問它,在JDK9中,public類型可能不是對每個類都公開的,模塊中定義的public類型可能分為3類:
- 僅在定義模塊內公開:如果一個類型在模塊中被定義為public,但是該模塊不導出包含該類型的包,則該類型僅在該模塊中是公開的,沒有其他模塊而已訪問類型;
- 只針對特定模塊公開:如果一個類型在一個模塊中被定義為public,但是該模塊使用一個限定的export來導出包含該類型的包,該類型將只能在有限導出的子句中指定的模塊中訪問;
- 指定所有類公開:如果一個類型在模塊中被定義為public,但該模塊使用包含該類型的非限定的導出語句導出該包,該類型將公開給讀取第一個模塊的每個模塊。
6.3 聲明依賴關系
模塊系統在編譯時以及運行時驗證模塊的依賴關系,有事希望在編譯時模塊依賴性是必需的,但在運行時是可選的。
需要(require)語句聲明當前模塊對另一個模塊的依賴關系,
requires <module>;
requires transitive <module>;
requires static <module>;
requires transitive static <module>;
static標示在編譯時的依賴是強制的,但在運行時是可選的:requires static N意味着模塊M需要模塊N,模塊N必須在編譯時出現才能編譯模塊M,而在運行時存在模塊N是可選的。
transitive當前模塊依賴其他模塊具有隱式依賴性,假設有三個模塊P,Q和R,假設模塊Q包含requires transitive R語句,如果模塊P包含requires Q,這意味着模塊P隱含依賴模塊R。
6.4 配置服務
Java允許使用服務提供者和服務使用者分離的服務提供者機制。JDK9運行使用語句uses和provides實現其服務。
use語句可以指定服務接口的名字,當前模塊就會發現它,使用java.util.ServiceLoader類進行加載:
uses <service-interface>
實例:
module M {
uses com.jdk9.prime.PrimeChecker;
}
com.jdk9.prime.PrimeChecker是一個服務接口,其實現類將由其他模塊提供,模塊M將使用java.util.ServiceLoader類來發現和加載此接口的實現。
provide語句指定服務接口的一個或多個服務廳程序實現類:
provide <service-interface> with <service-impl-class1>, <service-impl-class2>;
實例:
module P {
uses com.jdk9.CsvParser;
provides com.jdk9.CsvParser with com.jdk9.CsvParserImpl;
provides com.jdk9.prime.PrimeChecker with com.jdk9.prime.PrimeCheckerImpl;
}
7. 模塊描述符
7.1 編譯模塊聲明
模塊聲明存儲在名為module-info.java的文件中,該文件存儲在該模塊的源文件層次結構的根目錄下。
Java編譯器將模塊聲明編譯為名為module-info.class的文件。module-info.class文件被稱為模塊描述符。它被放置在模塊的編譯代碼層次結構的根目錄下。如果將模塊的編譯代碼打包到jar文件中,則module-info.class文件將存儲在jar文件的根目錄下。
7.2 模塊版本
在模塊系統的初始原型中,模塊聲明還包括模塊版本。包括模塊版本在聲明中使模塊系統的實現復雜化,所以模塊版本從聲明中刪除。模塊描述符(類文件格式)的可擴展格式被利用來向模塊添加版本。當將模塊的編譯代碼打包到jar中時,該jar工具提供了一個添加模塊版本的選項,最后將其添加到module-info.class文件中。
8. 打包模塊
模塊的artifact可以存儲在:
- 目錄中
- 模塊化的JAR文件中
- JMOD文件中,它是JDK9中引入的一種新的模塊封裝格式
8.1 目錄中的模塊
當模塊的編譯代碼存儲在目錄中時,目錄的根目錄包含模塊描述符(module-info.class文件),子目錄是包層次結構的鏡像。
8.2 模塊化JAR中的模塊
當JAR包含模塊的編譯代碼時,JAR稱為模塊化JAR。模塊化JAR在根目錄下包含一個module-info.class文件。
無論在JDk9之前使用JAR,現在都可以使用模塊化JAR。例如,模塊化JAR可以放置在類路徑上,在這種情況下,模塊化JAR中的module-info.class文件將被忽略,因為module-info中不是有效的類名。
8.3 JMOD文件中的模塊
JDK9引入了一種稱為JMOD的新格式來封裝模塊。JMOD文件使用.jmod擴展名。JDK模塊被編譯成JMOD格式,放在JDK_HOMEjmods目錄中。例如,可以找到一個包含java.base模塊內容的java.base.jmod文件。僅在編譯時和鏈接時才支持JMOD文件。它們在運行時不受支持。
9 模塊的類型
9.1 概述
舊的和新的應用程序將繼續使用未被模塊化或永遠不會被模塊化的庫,如果JDK9保持向后兼容性。在大多數情況下,在JDK8或更早版本中工作的應用程序將繼續在JDK 9中工作,為了簡化遷移,JDK9定義了4中類型的模塊:
- 普通模塊(Normal Modules)
- 開放模塊(Open Modules)
- 自動模塊(Automatic Modules)
- 未命名模塊(Unnameed Modules)

(1)一個模塊是代碼和數據的集合;
(2)基於模塊是否具有名稱,模塊可以是命名模塊或未命名模塊;
(3)沒有其他類別的未命名模塊;
(4)當模塊具有名稱時,可以在模塊聲明中明確指定名稱,或則可以自動(或隱式)生成名稱,如果名稱在模塊聲明中明確指定,則稱為顯式模塊,如果名稱由模塊系統通過讀取模塊路徑上的JAR文件名生成,則稱為自動模塊。
(5)如果不實用open修飾符的情況下聲明模塊,則稱為普通模塊;
(6)如果使用open修飾符聲明模塊,則稱為開放模塊。
開放模塊也是顯式模塊和命名模塊,自動模塊是一個命名模塊,因為它具有自動生成的名稱,但它不是顯式模塊,因為它在模塊系統在編譯時和運行時被隱式聲明。
9.2 普通模塊
使用模塊聲明明確而不實用open修飾符的模塊始終被賦予一個名稱,它被稱為普通模塊或簡化模塊。
9.3 開放模塊
模塊聲明包含open修飾符,則該模塊被稱為開放模塊。
9.4 自動模塊
為了向后兼容,查找類型的類路徑機制仍然可以在JDK9中使用,可以選擇將JAR放在類路徑、模塊路徑和兩者組合上。請注意,可以在模塊路徑和類路徑上放置模塊化JAR以及JAR。
將JAR放在模塊路徑上時,JAR被視為一個模塊,稱為自動模塊。
自動模塊其實也是一個有名字的模塊,其名稱和版本由JAR文件的名稱派生,規則如下:
- 刪除jAR文件的.jar擴展名,如果JAR文件名是com.jdk9.m-1.0.jar,則此步驟將刪除.jar擴展名,並通過以下步驟使用com.jdk9.m-1.0來推倒出模塊的名稱及其版本;
- 如果名稱以連字符后跟至少一個數字(也可后跟一個點),則模塊名稱將從最后一個連字符之前的名稱部分派生,如果它可以被解析為有效的版本,連字符后面的部分被分配為模塊的版本,在此示例中,模塊名稱將從com.jdk9.m派生,版本派生為1.0;
- 名稱部分中的每個非字母數字字符都被替換為一個點,並且在所得到的字符串中,用一個點替換兩個連續的點,此外,所有前導和后跟的點都被刪除。
下面列出了幾個JAR名稱,以及派生的自動模塊名稱和版本:
| JAR名詞 | 模塊名稱 |
| com.jdk9.m-1.0 | com.jdk9.m |
| junit-4.10 | junit |
| apache-logging1.5.0 | 有錯誤 |
| spring-core-4.0.1.RELEASE | spring.core |
| jdojo-tans-api_1.5_spec-1.0.0 | 有錯誤 |
| - | 有錯誤 |
如果無法從其名稱導出有效的自動模塊名稱,則放置在模塊路徑上的JAR將拋出異常:
java.lang.module.ResolutionException: Unable to derive module desciptor for: apache-logging1.5.0.jar
要有效使用的自動模塊,必須導出包並讀取其他模塊:
- 自動模塊讀取所有其他 模塊,在解析模塊圖之后,會添加自動OK到所有其他模塊,其他模塊都可以讀取自動模塊;
- 自動模塊中的所有包都被導出並打開
9.5 未命名模塊
可以將JAR和模塊化JAR放在類路徑上,當類型加載並且在任何已知模塊中到不到其包時,模塊系統會嘗試從類路徑加載類型。如果在類型路徑上找到該類型,它將由類加載器加載,並成為該類加載器的一個名為unnamed模塊的模塊成員。每個類加載器定義一個未命名的模塊,其成員是從類路徑加載的所有類型。一個未命名的模塊沒有名次,因此顯式模塊不能使用requires語句來聲明對它的依賴,如果有明確的模塊需要時會用未命名模塊中的類型,則必須通過將JAR放置在模塊路徑上,將未命名模塊的JAR用作自動模塊。
10 遷移到JDK9
在JDK9之前,一個有意義的Java應用程序由幾個駐留在三個層面的JAR組成:
- 開發人員開發的應用程序層中的程序JAR;
- 在類庫層中的類庫JAR--通常由第三方提供;
- JVM層中的Java運行時的JAR;
JDK9已經通過將Java運行時JAR轉換為模塊來模塊化,也就是說,Java運行時由模塊組成。
類庫層主要由放置在類路徑上的第三方JAR組成,如果要將應用程序遷移到JDk9,可能無法獲得第三方JAR的的模塊化版本,也無法控制供應商如果將第三方JAR轉換為模塊。所以,可以將庫JAR放在模塊路徑上,並將其視為自動模塊。
