編譯器與 SOURCEPATH


來解決原始碼檔案與位元碼檔案都放在一起的問題。請將 JavaGossip / Labs / SOURCEPATH-javac / 的Hello1資料夾複製至C:\workspace中,Hello1資料夾中有src與classes資料夾,src資料夾中有Console.java與Main.java兩個檔案,其中Console.java就是 編譯器與 CLASSPATHConsole類別的原始碼(目前你不用關心它如何撰寫),而Main.java的內容 編譯器與 CLASSPATH 中的Main.java相同。


src資料夾將用來放置原始碼檔案,而編譯好的位元碼檔案,希望能指定存放至classes資料夾。你可以在文字模式下,切換至Hello1資料夾,然後如下進行編譯:

指定-sourcepath與-d進行編譯


在編譯src/Main.java時,由於程式碼中要使用到Console類別,你必須告訴編譯器,Console類別的原始碼檔案存放位置,這邊使用-sourcepath指定從src資料夾中尋找原始碼檔案,而-d指定了編譯完成的位元碼存放資料夾,編譯器會將使用到的相關類別原始碼也一併進行編譯,編譯完成後,會在classes資料夾中看到Console.class與Main.class檔案。你可以如下執行程式:
指定執行classes中的Main類別


你可以在編譯時指定-verbose引數,看到編譯器進行編譯時的過程,這有助於了解SOURCEPATHCLASSPATH的差別,目前要瞭解的部份有 …

C:\workspace\Hello1>javac -verbose -sourcepath src -d classes src/Main.java
[parsing started RegularFileObject[src\Main.java]]
[parsing completed 32ms]
[search path for source files: src]
[search path for class files: C:\Program Files\Java\jdk1.8.0\jre\lib\resources.j
ar,C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar,C:\Program Files\Java\jdk1.8.0\
jre\lib\sunrsasign.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\jsse.jar,C:\Progra
m Files\Java\jdk1.8.0\jre\lib\jce.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\cha
rsets.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\jfr.jar,C:\Program Files\Java\j
dk1.8.0\jre\classes,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\access-bridge-64.
jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\cldrdata.jar,C:\Program Files\Jav
a\jdk1.8.0\jre\lib\ext\dnsns.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\jacc
ess.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\jfxrt.jar,C:\Program Files\Ja
va\jdk1.8.0\jre\lib\ext\localedata.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ex
t\nashorn.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\sunec.jar,C:\Program Fi
les\Java\jdk1.8.0\jre\lib\ext\sunjce_provider.jar,C:\Program Files\Java\jdk1.8.0
\jre\lib\ext\sunmscapi.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\sunpkcs11.
jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\zipfs.jar,.]


...

[wrote RegularFileObject[classes\Main.class]]
[checking Console]

...

[wrote RegularFileObject[classes\Console.class]]
[total 781ms]

在編譯時,會先搜尋-sourcepath指定的資料夾(上例指定src)是不是有使用到的類別原始碼,然後會搜尋CLASSPATH中,是否有已編譯的類別位元碼,你可以發現,其實預設搜尋位元碼的路徑包括許多預設的JAR檔案,像是rt.jar等,留意最後那那個「.」,由於沒有指定-classpath-cp),預設會搜尋目前路徑。


確認原始碼與位元碼搜尋路徑之後,接著檢查CLASSPATH中是否已經有編譯完成的Main類別,如果存在且從上次編譯後,Main類別的原始碼並沒有改變,則無需重新編譯,如果不存在,則重新編譯Main類別,就上例而言,由於CLASSPATH並不包括classes資料夾,所以找不到Main類別位元碼,因此重新編譯出Main.class並存放至classes中。

接著檢查CLASSPATH中是否已經有編譯完成的Console類別,如果存在且從上次編譯後,Console類別的原始碼並沒有改變,則無需重新編譯,如果不存在,則重新編譯Console類別,就上例而言,由於CLASSPATH並不包括classes資料夾,所以找不到Console類別位元碼,因此重新編譯出Console.class並存放至classes中。


實際專案中會有數以萬計的類別,如果每次都要重新將.java編譯為.class,那會是非常費時的工作,所以編譯時若類別路徑中已存在位元碼,且上次編譯後,原始碼並沒有修改,無需重新編譯會比較節省時間,因此,就上例而言,應該指定-cp為classes。例如:

C:\workspace\Hello1>javac -verbose -sourcepath src -cp classes -d classes src/Main.java
[parsing started RegularFileObject[src\Main.java]]
[parsing completed 32ms]
[search path for source files: src]
[search path for class files: C:\Program Files\Java\jdk1.8.0\jre\lib\resources.j
ar,C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar,C:\Program Files\Java\jdk1.8.0\
jre\lib\sunrsasign.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\jsse.jar,C:\Progra
m Files\Java\jdk1.8.0\jre\lib\jce.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\cha
rsets.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\jfr.jar,C:\Program Files\Java\j
dk1.8.0\jre\classes,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\access-bridge-64.
jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\cldrdata.jar,C:\Program Files\Jav
a\jdk1.8.0\jre\lib\ext\dnsns.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\jacc
ess.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\jfxrt.jar,C:\Program Files\Ja
va\jdk1.8.0\jre\lib\ext\localedata.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ex
t\nashorn.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\sunec.jar,C:\Program Fi
les\Java\jdk1.8.0\jre\lib\ext\sunjce_provider.jar,C:\Program Files\Java\jdk1.8.0
\jre\lib\ext\sunmscapi.jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\sunpkcs11.
jar,C:\Program Files\Java\jdk1.8.0\jre\lib\ext\zipfs.jar,classes]


...

[loading RegularFileObject[classes\Console.class]]
[wrote RegularFileObject[classes\Main.class]]
[total 687ms]

這次指定了-sourcepath為src,而-cp為classes,所以會在src中搜尋位原始碼檔案,在classes中搜尋位元碼檔案(注意最後的classes),由於CLASSPATH中包括classes資料夾,所以找到Console類別位元碼,因此無需重新編譯Console.class,而只編譯javac指定的Main.java為Main.class。