一、前言
Java 的模組化系統 (Project Jigsaw),正式在 Java 9 中引入,對 Java 的開發方式帶來了重大的改變。在 Java 7 和 8 之前,Java 開發經常會遇到以下問題:
- 大型應用程式的類別路徑混亂: 龐大的應用程式往往會使用大量的 JAR 檔案,導致類別路徑 (Classpath) 變得難以管理,容易發生版本衝突和命名空間衝突。
- 封裝性不足: 缺乏明確的介面和實作分離機制,容易讓內部實作細節暴露,降低程式碼的維護性和安全性。
- 運行時環境臃腫: 即使應用程式只用到 Java SE API 的一部分,仍然需要引入整個 JDK,造成運行時資源的浪費。
模組化系統旨在解決這些問題,透過明確的模組定義,改善程式碼的封裝性、可維護性和效能。以下將整理 Java 7 至 17 間關於模組化的新特性及其相關內容。
目錄
- 一、前言
- 二、模組化概念與架構 (Java 9)
- 三、模組化的優點
- 四、模組化的工具 (Java 9)
- 五、將模組打包成 JAR 檔案
- 六、Module 工具:jmod 與 jlink
- 七、Java Module Info 中的關鍵字
- 八、Java 9 之後的模組化演進 (Java 10 - 17)
- 九、總結
二、模組化概念與架構 (Java 9)
Java 9 引入了模組化系統,核心概念是「模組」(Module)。模組是一個命名過的、自描述的程式碼和資源的集合,它定義了:
-
模組名稱 (Module Name):
唯一標識模組的名稱。
建議使用套件 (package) 命名慣例,例如
com.example.mymodule
。 - 導出 (Exports): 模組向外暴露的 API (程式碼)。
- 依賴 (Requires): 模組所依賴的其他模組。
模組架構示意圖
graph LR A[Module A] --> B(Module B: Requires A) A --> C(Module C: Requires A) B --> D[Module D: Requires B, C] C --> D E[Module E] F[Module F: Requires E] subgraph Module Container A B C D E F end
此圖展示模組間的依賴關係:
- Module A: 可以被 B 和 C 模組使用。
- Module B & C: 都依賴於 Module A。
- Module D: 依賴於 Module B 和 C。
- Module E: 是一個獨立的模組。
- Module F: 依賴於 Module E。
- Module Container: 代表整個模組系統的容器。
module-info.java
檔案
每個模組都必須包含一個
module-info.java
檔案,用於描述模組的資訊。以下是
module-info.java
中常用的關鍵字及其說明:
-
module
: 模組定義檔案以module
關鍵字開始,後面跟著模組的名稱和定義。 -
requires
: 用於指示此模組依賴的其他模組;在此關鍵字後面必須指定模組名稱。 -
requires transitive
: 在requires
關鍵字後指定transitive
,表示任何依賴此模組的模組都將隱式依賴被transitive
宣告的模組。 -
exports 及 exports ... to ...
: 用於指示模組內哪些套件可以公開訪問;在此關鍵字後必須指定套件名稱。exports…to 指令可讓您精確地指定哪些模組或模組的程式碼可以存取匯出的套裝程式,這稱為qualified export
。 -
open, opens 及 opens ... to ...
: 在 Java 9 之前,reflection 可用於瞭解套裝程式中的所有類型以及類型的所有成員 (甚至是其 private 成員) ,無論您是否允許此功能。因此,沒有任何東西被真正封裝。在 Java 9 後模組化是個強封裝,只能透過此關鍵字指示模組中哪些套件僅在運行時可以存取,並且可通過反射 API 進行內省;這對於像 Spring 和 Hibernate 這樣高度依賴反射 API 的函式庫非常重要。opens
也可以在模組層級使用,在這種情況下,整個模組都可以在運行時存取。 -
uses
: 用於指示此模組使用的服務介面;在此關鍵字後必須指定型別名稱,即完整的類別/介面名稱。 -
provides ... with ...
: 用於指示此模組提供服務介面的實作,with
關鍵字後面的部分表示服務介面的實作。
其基本語法如下:
-
module com.example.mymodule
: 聲明模組的名稱為com.example.mymodule
。 -
exports com.example.mymodule.publicapi
: 導出com.example.mymodule.publicapi
套件下的類別。 -
requires java.sql
: 聲明依賴java.sql
模組。 -
requires transitive com.example.dependency
: 聲明依賴com.example.dependency
模組,並且該依賴會被傳遞給其他依賴com.example.mymodule
的模組。 -
opens com.example.mymodule.internal
: 聲明com.example.mymodule.internal
套件可以反射存取。 -
uses com.example.mymodule.MyService
: 聲明使用com.example.mymodule.MyService
服務介面。 -
provides com.example.mymodule.MyService with com.example.mymodule.MyServiceImpl
: 聲明提供com.example.mymodule.MyService
的實作com.example.mymodule.MyServiceImpl
。
三、模組化的優點
模組化的主要優點包含:
-
更強的封裝性:
透過
exports
聲明,可以明確控制哪些 API 可以被其他模組使用,避免內部實作細節洩漏,提升程式碼的安全性。 -
更明確的依賴管理:
使用
requires
聲明,可以清楚知道模組之間的依賴關係,減少類別路徑衝突,更容易追蹤和管理依賴。 - 更小的運行時環境: 模組化可以讓 Java 運行時只載入必要的模組,減少資源消耗,提升效能。
- 更高的可維護性: 模組化程式碼更容易理解和維護,也更容易進行程式碼的重構。
-
Zulu Java 17 有哪些模組呢?使用
java --list-modules
來確認。
四、模組化的工具 (Java 9)
Java 9 提供了相關工具來管理模組,以下列出幾個重要的工具:
-
javac
: 編譯器,用於編譯模組化的 Java 程式碼,可以根據module-info.java
檔案來處理模組依賴。 -
java
: 執行器,用於執行模組化的 Java 程式碼,可以根據模組路徑找到模組和依賴。 -
jlink
: 連結器,可以將 JDK 中的模組和應用程式的模組組合在一起,生成自包含的運行時映像,减少運行時环境的大小。 -
jmod
: 模組打包工具,用於建立模組檔案。
範例:編譯和執行模組化程式碼
假設有兩個模組
com.example.modulea
和
com.example.moduleb
,且
com.example.moduleb
依賴
com.example.modulea
:
-
這個目錄結構展示了模組化專案的組織方式。
-
bin
目錄用於存放模組化的 JAR 檔案。 -
modules
目錄用於存放編譯後的模組檔案。 -
src
目錄用於存放原始碼。 -
com.example.modulea
和com.example.moduleb
分別是兩個模組的原始碼目錄。 -
每個模組的目錄下都有
module-info.java
檔案,以及各自的程式碼檔案。
-
com/example/modulea/module-info.java
-
這個
module-info.java
檔案定義了com.example.modulea
模組。-
module com.example.modulea
:聲明了模組的名稱。 -
exports com.example.modulea.publicapi
:導出了com.example.modulea.publicapi
這個套件,表示其他模組可以訪問此套件下的類別。 -
重點:
沒有導出
com.example.modulea.internal
套件,這意味著com.example.modulea
模組內部的程式碼不會被其他模組直接訪問。
-
com/example/modulea/publicapi/PublicClass.java
-
這個 Java 類別
PublicClass
位於com.example.modulea.publicapi
套件下,它有一個sayHello
方法,會印出 "Hello from Module A!"。-
這個類別是
com.example.modulea
模組公開的 API。
-
這個類別是
com/example/modulea/internal/InternalClass.java
-
這個 Java 類別
InternalClass
位於com.example.modulea.internal
套件下,它有一個saySecret
方法,會印出 "This is a secret from Module A!"-
這個類別是
com.example.modulea
模組內部的實作,沒有被導出。
-
這個類別是
com/example/moduleb/module-info.java
-
這個
module-info.java
檔案定義了com.example.moduleb
模組。-
module com.example.moduleb
:聲明了模組的名稱。 -
requires com.example.modulea
:表示com.example.moduleb
模組依賴於com.example.modulea
模組。
-
com/example/moduleb/Main.java
-
這個 Java 類別
Main
位於com.example.moduleb
套件下,是com.example.moduleb
模組的主要程式進入點。-
它引入了
com.example.modulea.publicapi.PublicClass
,並建立實例並調用sayHello()
方法,展示了如何使用公開的模組 API。 -
注意:
com.example.modulea.internal.InternalClass
被註解,因為com.example.modulea
模組並未導出com.example.modulea.internal
套件,所以無法直接使用。
-
它引入了
編譯
com.example.modulea
成功:
-
這個命令使用
javac
編譯器編譯com.example.modulea
模組。-
-encoding utf-8
:設定原始碼的編碼為 UTF-8,避免中文等特殊字元出現問題。 -
-d modules
:指定編譯後的輸出目錄為modules
,編譯後的模組相關.class
檔案會輸出到modules
目錄底下,並且會自動以模組名稱建立子資料夾。 -
--module com.example.modulea
:指定要編譯的模組名稱。 -
--module-source-path src
:指定模組原始碼的目錄為src
,javac
會在此目錄下尋找模組的程式碼。 -
執行後結果:
會在
modules
目錄下建立com.example.modulea
目錄,並將編譯後的檔案 (.class
檔案以及module-info.class
) 放入其中。
-
編譯
com.example.moduleb
失敗:
錯誤:找不到
com.example.modulea.internal
套件,因為com.example.modulea模組沒有
exports
開放給其他模組使用
com.example.modulea.internal
套件。
-
這個命令嘗試編譯
com.example.moduleb
模組,使用了與編譯com.example.modulea
相似的選項。-
但是由於
com.example.moduleb/Main.java
中嘗試引入和使用com.example.modulea.internal.InternalClass
,而com.example.modulea
模組並未導出該套件,因此編譯器會報錯,指出找不到com.example.modulea.internal
套件。 - 重點: 這個錯誤示範了模組化的封裝性,未導出的套件無法被其他模組使用。
-
但是由於
調整修改
com/example/moduleb/Main.java
,將未
exports
套件註解,再次編譯
-
修改了
Main.java
檔案,將com.example.modulea.internal.InternalClass
的引入和使用程式碼註解起來,使其不再嘗試使用未導出的套件。
編譯
com.example.moduleb
成功:
-
這個命令再次編譯
com.example.moduleb
模組。-
由於
Main.java
中不再使用未導出的套件,編譯成功。 -
執行後結果:
會在
modules
目錄下建立com.example.moduleb
目錄,並將編譯後的檔案放入其中。
-
由於
執行:
輸出結果:
-
這個命令使用
java
執行器執行com.example.moduleb
模組的主要類別。-
-p "modules/"
:指定模組路徑,告訴java
執行器從modules
目錄中尋找模組。 -
-m "com.example.moduleb/com.example.moduleb.Main"
:指定要執行的模組和主類別,格式為模組名/主類別的完整類別名
。 -
執行後結果:
com.example.moduleb.Main
類別會被執行,它會調用com.example.modulea.publicapi.PublicClass
的sayHello()
方法,輸出Hello from Module A!
。
-
執行完成後的目錄結構:
五、將模組打包成 JAR 檔案
除了直接執行模組化的程式碼,也可以將模組打包成 JAR 檔案,方便部署和管理。以下示範如何將
com.example.modulea
和
com.example.moduleb
模組打包成 JAR 檔案,並使用 JAR 檔案來執行程式。
-
打包
com.example.modulea
模組:-
這個命令使用
jar
工具將com.example.modulea
模組打包成 JAR 檔案。-
--create
:表示建立新的 JAR 檔案。 -
--file=bin/com.example.modulea.jar
:指定 JAR 檔案的輸出路徑和名稱為bin/com.example.modulea.jar
。 -
--module-version=1.0
:設定模組的版本號為 1.0。 -
-C modules/com.example.modulea/ .
:表示從modules/com.example.modulea/
這個目錄讀取所有內容,包括編譯後的.class
文件和module-info.class
檔案。
-
-
執行後結果:
會在當前目錄下建立
bin
資料夾,並在其中生成com.example.modulea.jar
檔案。
-
這個命令使用
-
打包
com.example.moduleb
模組:-
這個命令與打包
com.example.modulea
模組類似,只是打包的是com.example.moduleb
模組,並將輸出路徑設定為bin/com.example.moduleb.jar
。 -
執行後結果:
會在當前目錄下建立
bin
資料夾,並在其中生成com.example.moduleb.jar
檔案。
-
這個命令與打包
-
使用 JAR 檔案執行程式:
輸出結果:
-
這個命令使用
java
執行器執行com.example.moduleb
模組的主要類別,但這次是使用 JAR 檔案的方式。-
-p "bin/com.example.modulea.jar;bin/com.example.moduleb.jar"
:指定模組路徑,告訴java
執行器從bin
目錄中的 JAR 檔案中尋找模組,多個 JAR 檔案之間用分號;
分隔。 -
-m "com.example.moduleb/com.example.moduleb.Main"
:指定要執行的模組和主類別。 -
執行後結果:
com.example.moduleb.Main
類別會被執行,它會調用com.example.modulea.publicapi.PublicClass
的sayHello()
方法,輸出Hello from Module A!
。
-
-
使用
-p
選項指定 JAR 檔案的路徑,而不是目錄。 -
其他執行方式不變。
-
執行完成後的目錄結構:
六、Module 工具:jmod 與 jlink
(6.1) jmod:模組封裝工具
JDK 9 引入了一種稱為JMOD的新格式來封裝模塊。
jmod
主要用於
建立和檢查模組的描述檔
,以及
打包模組的各種資源
。簡單來說,
jmod
讓你將模組的程式碼、資源、原生程式庫、文件等整理成一個單一的
.jmod
檔案,方便管理和分發,可以在JDK_HOME/jmods目錄中找到它們。
-
jmod 的作用 javase-17-jmod :
-
建立模組描述檔:
.jmod
檔案內包含module-info.class
,定義了模組的名稱、相依性、公開的套件等資訊。 -
打包資源:
可以將類別檔案、資源檔、原生程式庫、以及其他相關檔案打包進
.jmod
檔案。 - 管理版本: 可以儲存模組版本資訊。
- 文件管理: 可以打包模組的 API 文件。
-
建立模組描述檔:
-
jmod 的基本用法:
-
jmod create
: 建立新的.jmod
檔案。 -
jmod list
: 列出.jmod
檔案內的內容。 -
jmod describe
: 顯示.jmod
檔案的模組描述資訊。 -
--class-path
: 指定尋找模組class的路徑或模組jar檔案。 -
<jmod-file>
: 指定.jmod
檔案的路徑。
-
-
jmod 範例:
我們將 com.example.modulea 和 com.example.moduleb 打包成 jmod:
-
查看jmod內容:
-
查看jmod模組描述:
此範例會將編譯後的
.class
檔案與module-info.class
一起打包進jmods/com.example.modulea.jmod
與jmods/com.example.moduleb.jmod
。
(6.2) jlink:連結器
jlink
主要用於
建立自訂的、最小化的 JRE (Java Runtime Environment)
。它可以根據你的應用程式所需要的模組,建立一個只包含必要模組的 JRE。這有助於減少應用程式的部署大小,以及提高執行效率。
-
jlink 的作用:
- 客製化 JRE: 可以根據專案的模組需求,建立客製化的 JRE。
- 減少部署大小: 只包含應用程式所需的模組,減少 JRE 的體積。
- 提高執行效率: 減少 JVM 加載時間和記憶體佔用。
-
jlink 的基本用法:
-
--module-path
: 指定尋找模組的路徑。 -
--add-modules
: 指定要包含在 JRE 中的模組。 -
--output
: 指定輸出 JRE 的目錄。
-
-
jlink 範例:
-
假設我們已經有一個模組的 jar檔
mymodule.jar
(你可以用上面的範例,然後把out
目錄下的com
目錄打包成 jar檔)。 -
建立自訂 JRE:
-
$JAVA_HOME
必須是你電腦上 JDK 的安裝目錄。 -
./jmods
為com.example.modulea.jmod
與com.example.moduleb.jmod
的所在目錄。 -
這裡我們指定
java.base
(Java 核心模組)以及我們自訂的com.example.modulea
和com.example.moduleb
模組。
-
-
執行應用程式:
執行結果
此範例會建立一個名為
myjre
的目錄,裡面包含一個只包含java.base
、com.example.modulea
和com.example.moduleb
的 JRE。要分發我們自己的Java應用程序,只需要將這個jre目錄打個包給對方發過去,對方直接運行上述命令即可,既不用下載安裝JDK,也不用知道如何配置模組,非常方便分發和部署。
-
(6.3) 工具簡述:
-
jmod
用於封裝和管理模組檔案,類似於將模組打包成一個容器,便於儲存和分發。 -
jlink
用於建立自訂的、精簡的 JRE,以便應用程式部署時,只需要必要的模組,減少體積。
執行完成後的目錄結構:
七、Java Module Info 中的關鍵字
在 Java 9 引入模組化系統 (Java Platform Module System, JPMS) 後,模組之間的封裝性是核心概念之一。模組的
module-info.java
檔案中,除了使用
exports
、
requires
之外,還可以其他更進階的關鍵字用法,以下我們來一一介紹。
(7.1) opens
-
允許反射存取:
opens
關鍵字允許其他模組,透過反射機制 (Reflection API) 來存取被指定的套件內的 所有類型 (包括公開、預設、保護、私有),以及它們的所有成員 (包括公開、預設、保護、私有)。 -
鬆綁封裝: 與
exports
僅允許其他模組存取公開類別和成員不同,opens
打開了更廣泛的存取權限,允許其他模組繞過模組的正常封裝限制。 -
特殊情境使用:
opens
通常用於:- 框架/工具開發: 像是 Jackson、Gson 等 JSON 處理庫,或 Hibernate 等 ORM 框架,需要利用反射來操作對象,讀取私有欄位和方法。
- 測試: 測試框架可能需要反射來存取應用程式的私有部分,以便進行更深入的測試。
- 動態代理: 創建動態代理的程式碼,也可能需要反射存取目標物件的私有成員。
-
opens
的語法-
<module-name>
: 您的模組名稱。 -
<package-name>
: 您要開放反射存取的套件名稱。 -
to <module-name1>, <module-name2>
(可選): 將反射存取權限限制於特定模組。若省略to
子句,則代表開放給所有模組。
-
-
範例: 假設我們有一個名為
com.example.mymodule
的模組,其中有一個com.example.mymodule.internal
套件,裡面有一些內部使用的類別。-
開放給所有模組:
此範例中,
com.example.mymodule.internal
中的所有類別和成員,都可以被其他模組透過反射存取。 -
開放給特定模組:
此範例中,只有
com.example.othermodule
可以反射存取com.example.mymodule.internal
中的類別和成員。其他模組則無法進行反射存取。
-
-
注意事項
-
謹慎使用
opens
: 由於opens
會繞過模組的封裝機制,因此應該謹慎使用,只在必要的狀況下才使用。濫用opens
會破壞模組化的好處,降低程式碼的可維護性與安全性。 -
opens
與exports
的差異:-
exports
: 允許其他模組 正常存取 (編譯時和執行時) 指定套件中的 公開類別和成員 。 -
opens
: 允許其他模組透過 反射 存取指定套件中的 所有類型和成員 。
-
-
反射風險: 儘管反射提供靈活性,但使用反射會增加程式碼的複雜度和維護難度,並可能造成執行時錯誤。
-
(7.2) uses
uses
關鍵字用於聲明一個模組
依賴
某個
服務介面 (service interface)
,但
不直接依賴
實作該介面的特定模組。它揭示了模組使用服務的意圖,但將服務的實際提供者 (implementation) 的選擇延遲到執行時。
-
uses
的用途-
服務發現 (Service Discovery):
uses
配合provides...with...
關鍵字,是 Java 模組系統中實現服務發現機制的基礎。 -
解耦 (Decoupling):
uses
讓模組可以依賴介面,而不直接依賴於提供該介面的具體實現,達到解耦的效果。這使得更換或擴充實作方式更加容易,而不需要修改依賴的模組。 -
延遲綁定 (Late Binding):
uses
並不會在編譯時確定哪個模組提供了服務,而是在執行時由 Java 模組系統根據模組圖的配置來確定。 -
可插拔 (Pluggable) 系統:
透過
uses
和provides
,我們可以建立可插拔的系統,其中不同的模組可以提供不同的服務實現,而應用程式可以在執行時動態地選擇它們。
-
服務發現 (Service Discovery):
-
uses
的語法-
<module-name>
: 您的模組名稱。 -
<service-interface-name>
: 您要依賴的服務介面的完整名稱 (fully qualified name)。這個介面通常會被宣告為public
。
-
-
範例
假設我們有以下三個模組:
-
com.example.myserviceapi
: 定義服務介面的模組 -
com.example.myclient
: 使用服務的模組 (消費者) -
com.example.myserviceimpl
: 提供服務的模組 (提供者) -
說明
-
com.example.myclient
模組使用uses
宣告它依賴com.example.myserviceapi.MyService
介面。 -
com.example.myserviceimpl
模組使用provides...with...
宣告它提供了com.example.myserviceapi.MyService
的實作,並且實作類別為com.example.myserviceimpl.MyServiceImpl
。 -
在執行時,
ServiceLoader
會根據module-info.java
的uses
和provides
聲明來找出可用的MyService
實作並注入。
-
-
-
注意事項
-
uses
關鍵字本身不執行任何注入行為,它只是聲明對服務的依賴。實際的服務加載由java.util.ServiceLoader
或其他相關的服務加載機制完成。 -
一個模組可以
uses
多個服務介面。 -
一個服務介面可以被多個模組
uses
。 -
uses
的使用,通常會搭配provides...with...
一起使用,以實現服務發現和解耦。
-
(7.3) provides...with...
provides...with...
關鍵字用於聲明一個模組
提供
某個
服務介面 (service interface)
的
具體實作
。它與
uses
關鍵字搭配使用,共同構成了 Java 模組系統中服務發現機制的基礎。
-
provides...with...
的用途-
服務提供 (Service Provision):
provides...with...
聲明了某個模組提供了特定服務介面的實作。 -
服務發現 (Service Discovery):
結合
uses
,provides...with...
讓模組系統在執行時可以找到滿足服務依賴的實作模組。 -
實現解耦 (Decoupling):
透過
provides...with...
,模組可以提供服務的實作,而不需要直接依賴於使用該服務的模組,達到解耦的效果。 -
可插拔系統 (Pluggable Systems):
provides...with...
允許不同的模組提供同一服務介面的不同實作,使得系統可以根據需求選擇合適的實作,增加了系統的靈活性。 -
延遲綁定 (Late Binding):
provides...with...
不會在編譯時綁定具體實作,而是延遲到執行時由模組系統來解析。
-
服務提供 (Service Provision):
-
provides...with...
的語法-
<module-name>
: 您的模組名稱。 -
<service-interface-name>
: 您要提供的服務介面的完整名稱 (fully qualified name)。這個介面通常會被宣告為public
。 -
<implementation-class-name>
: 提供服務介面實作的類別的完整名稱 (fully qualified name)。這個類別必須實作<service-interface-name>
介面,且通常會被宣告為public
。
-
-
**範例:**假設我們有以下三個模組:
-
com.example.myserviceapi
: 定義服務介面的模組 -
com.example.myclient
: 使用服務的模組 (消費者) -
com.example.myserviceimpl
: 提供服務的模組 (提供者) -
說明
-
com.example.myserviceimpl
模組使用provides com.example.myserviceapi.MyService with com.example.myserviceimpl.MyServiceImpl;
宣告它提供了com.example.myserviceapi.MyService
介面的實作,並且具體實作類別為com.example.myserviceimpl.MyServiceImpl
。 -
com.example.myclient
模組使用uses com.example.myserviceapi.MyService;
宣告它依賴com.example.myserviceapi.MyService
介面。 -
在執行時,
ServiceLoader
會根據module-info.java
的uses
和provides...with...
聲明,找到MyService
的實作,並將其實例返回給客戶端模組。
-
-
-
注意事項
-
一個模組可以提供多個服務介面的實作,只需要使用多個
provides...with...
語句。 - 一個服務介面可以被多個模組實作,Java 模組系統會根據模組圖的配置選擇合適的實作。
-
provides...with...
通常會與uses
關鍵字一起使用。
-
一個模組可以提供多個服務介面的實作,只需要使用多個
八、Java 9 之後的模組化演進 (Java 10 - 17)
Java 9 引入模組化系統之後,後續的版本主要針對模組化做了一些細微的改進和優化,沒有引入革命性的改變。主要集中在以下幾點:
- 模組化的微調: 例如改進模組的依賴管理、導出的機制。
- 與其他新特性的整合: 確保模組化系統與 Java 的其他新特性兼容。
- 效能優化: 針對模組化的運行時效能做優化。
值得注意的是,Java 17 中,模組化系統已經趨於穩定,成為 Java 應用開發的重要組成部分。
九、總結
Java 模組化 (Module) 是 Java 開發中一個重要的里程碑,解決了傳統 Java 開發中許多問題,帶來了更強的封裝性、更明確的依賴管理、更小的運行時環境,和更高的可維護性。雖然 Java 9 是模組化的關鍵版本,之後的版本主要在細節上進行優化和調整,但模組化的重要性不容忽視。對於現代 Java 開發,模組化是基礎且重要的知識,可以讓開發者建構更穩健、高效且可維護的應用程式。理解和掌握模組化的概念和使用,將對 Java 開發者有莫大的幫助。
沒有留言:
張貼留言