Contents

Java 的 Classpath 程是編譯順序

Java 的 Classpath 是 JVM 找到類別檔(.class)和 JAR 的依據,理解它對於編譯與執行 Java 程式都非常重要。

什麼是 Classpath?

Classpath 告訴 Java 編譯器(javac)和 JVM(java)在哪裡尋找:

  • .class 檔(編譯後的位元組碼)
  • .jar 檔(打包的類別庫)
  • 資源目錄(包含 .properties.xml 等設定檔)

如果 Classpath 設定不正確,執行時會拋出 ClassNotFoundExceptionNoClassDefFoundError


設定 Classpath 的方式

1. 使用 -cp-classpath 參數(推薦)

編譯時:

1
javac -cp lib/foo.jar:lib/bar.jar src/Main.java

執行時:

1
java -cp .:lib/foo.jar:lib/bar.jar com.example.Main

Windows 環境使用分號 ; 分隔,Unix/Linux/macOS 使用冒號 : 分隔。

2. 設定環境變數 CLASSPATH

1
2
3
4
5
# Linux/macOS
export CLASSPATH=.:lib/foo.jar:lib/bar.jar

# Windows(命令提示字元)
set CLASSPATH=.;lib\foo.jar;lib\bar.jar

⚠️ 不建議設定全域環境變數 CLASSPATH,因為會影響系統所有 Java 程式,容易產生衝突。


多個 JAR 的 Classpath 設定

當依賴的 JAR 很多時,可以使用萬用字元(Java 6+):

1
2
3
4
5
# 將 lib 目錄下所有 jar 加入 Classpath
java -cp "lib/*" com.example.Main

# 同時包含目前目錄和 lib 目錄下所有 jar
java -cp ".:lib/*" com.example.Main

注意:萬用字元 * 只會展開 .jar 檔,不會遞迴子目錄。


編譯期 vs 執行期的 Classpath 差異

階段 工具 說明
編譯期 javac -cp 需要包含所有被引用類別的 jar,讓編譯器能找到型別定義
執行期 java -cp 需要包含實際執行時用到的所有 jar,缺少會在 Runtime 拋出例外

範例:只有 provided scope 的 jar(如 Servlet API),編譯時需要但執行時由容器(Tomcat)提供,不需加入部署的 classpath。


類別載入順序

JVM 的類別載入器(ClassLoader)有階層結構:

  1. Bootstrap ClassLoader:載入 Java 核心類別(java.lang.* 等),對應 $JAVA_HOME/lib
  2. Extension ClassLoader:載入 $JAVA_HOME/lib/ext 下的類別
  3. Application ClassLoader:載入使用者指定的 Classpath

載入順序遵循「雙親委派模型」,先由上層嘗試載入,找不到才交給下層。若同一個類別名稱出現在多個 JAR 中,Classpath 排在前面的 JAR 優先被載入。


常見問題排查

1
2
3
4
5
6
# 確認目前使用的 Java 版本與 JAVA_HOME
java -version
echo $JAVA_HOME

# 執行時加上 verbose:class 觀察類別載入過程
java -verbose:class -cp ".:lib/*" com.example.Main

參考資料