具名模組、未具名模組


在〈淺談 Java 模組平臺系統〉中,談到了如何建立、編譯、運行簡單的模組,在模組描述檔的設定中,知道模組必須 exports 套件,另一個模組在 requires 了該模組之後,才能使用那個套件中的類別,你認識了模組路徑,也知道了在類別路徑下被發現的類別,都會被自動歸類到未具名模組,在模組路徑下被發現的類別,則都屬於某個具名模組,如果應用程式採取模組化設計,預設就會依賴在 java.base 模組之上, java.base 模組 exports 了它全部的套件,任何模組都可以讀取 java.base 模組。

具名模組、未具名模組

在 JDK8 或舊版本中,public 表示各套件間,也就是整個應用程式都可以存取,然而,在 JDK9 之後多了模組,模組中的 public 類別、方法、值域是否能被另一模組看見,還要視模組設定而定。

在定義模組時必須記得,同一個套件不可以同時出現在被 requires 的兩個模組中,這稱為分裂套件(Split package)會導致編譯失敗,而執行時期會產生 LayerInstantiationException

就算是 Java SE API,若不是 java.base 模組中的類別,還是必須 requires 相關的模組,例如,要使用內建日誌 API 的話,必須 requires java.logging,想使用 JDBC API 的話,必須 requires java.sql

許多程式庫都還沒有模組化,那麼該怎麼使用這個 JAR 檔案呢?暫時放在類別路徑上的話,會成為未具名模組,因為未具名模組沒有名稱,如果你的應用程式採取模組化設計,就沒有辦法在模組描述檔中 requires 了,這時候程式碼想使用類別路徑上的 API 就會發生編譯錯誤了。

不過,如果程式碼中不會依賴在到任何 JAR 檔案的 API,例如 JDBC 驅動程式類別是採用反射(Reflection)來載入,之後透過 JDBC 標準 API 來撰寫程式,因此將驅動程式的 JAR 放在類別路徑上,就算採取模組化設計,只要 requires java.sql,之後就可以使用 JDBC 載入驅動程式進行資料庫連線。

你也許只是想讓既有的應用程式跑在 JDK9 上,不打算模組化,也就是從程式進入點開始的每個類別,都是基於類別路徑,也就是都在未具名模組之中,然而要注意的是,對於其他同樣是放在類別路徑上的 JAR,在 API 的使用上也許沒有問題,使用的是 java.sqljava.util.logging 套件中的 API 也 OK,然而若使用到 javax.xml.bind.*javax.rmi 等套件,還是會出現編譯錯誤。

這是因為這些套件雖然包含在 Java SE 中,然而實際上是與 Java EE 相關的 API,在 JDK9 中還是有這些套件,不過被劃分到 java.se.ee 模組,JVM 預設並不會載入這個模組,因此編譯與執行時,必須使用 --add-modules java.se.ee,才找得到這些套件。