【java】Java之classpath

來源:互聯網
上載者:User

http://www.cnblogs.com/wxf0701/archive/2008/08/23/1274579.html

1.class搜尋路徑的重要性 
     理解class搜尋路徑對所有Java開發人員來說都很重要,但是,IDE的廣泛使用掩蓋了這項技術,使大家普遍對它缺乏瞭解,甚至包括好多老鳥。這個問題在開發分布式應用時尤其嚴重,因為應用程式運行時的系統內容可能和開發時的大不相同。 
     本文詳細描述了某些Java類被其他代碼引用時,Java編譯器和JVM如何使用類搜尋路徑定位這些類。這兒用一個非常簡單的例子——同一個包
中的兩個類——來具體說明。我們將通過不同的方式來編譯這兩個類,根據classpath的設定不同,編譯可能成功也可能失敗。 
     為了最清楚的說明這個問題,我們將只使用命令列工具進行編譯。互動式開發工具有它們自己操作classpath的方法,這些方法因產品而異。 
     至於是由Java編譯器在編譯時間定位需要的類,還是由JVM在運行時來做,這兩種方法沒有本質的區別。但編譯器可以從原始碼中編譯需要的類,而JVM不行。下面的例子中我們用編譯器來做,但在運行時的實現也完全類似。
2.執行個體
     本例有兩個很小的類:com.web_tomorrow.CPTest1 和 com.web_tomorrow.CPTest2,如下所示: 
     

     package com.web_tomorrow;

     public class CPTest1 {

         public static void main(String[] args) {

            System.out.println ("Run CPTest1.main()");

         }

     }     

     package com.web_tomorrow;

     public class CPTest2 {

          public static void main(String[] args) {

              System.out.println ("Run CPTest2.main()");

              CPTest1 cpt1 = new CPTest1();

         }

     }

     Java程式碼群組織的一個最基本規則就是`package name =
directory name'(“包名 =
目錄名”)。我們將為這兩個類建立對應的目錄結構,它們在包com.web_tomorrow中,所以我們建立目錄
com/web_tomorrow來存放原始碼:      

     [root] com 
     web_tomorrow 
     CPTest1.java 
     CPTest2.java 
     在本文中我用符號`[root]'來表示存放上述結構的任意目錄,也就是說,根目錄的位置由你決定。當然這會隨安裝這些檔案的方式不同而不一樣。
3.基本原理 
     讓我們來嘗試用命令列工具javac來編譯CPTest1.java。為了完全禁止類搜尋路徑(以防已有的設定影響本例),我們在javac上加上選項`-classpath ""'。 
     作為第一次實驗,我們先換到CPTest1.java所在的目錄下,並且嘗試用javac和檔案名稱進行編譯。 
     cd [root]/com/web_tomorrow 
     javac -classpath "" CPTest1.java 
     操作成功,因為編譯器能發現CPTest1.java
(它就在當前的工作目錄下),並且CPTest1沒有引用任何其他類。輸出檔案CPTest1.class在CPTest1.java的相同目錄下,因為
你沒有給編譯器任何資訊讓它作其它處理。到現在為止,一直都很好。現在讓我們用同樣的方式來試一下CPTest2。仍然在web_tomorrow目錄
下,執行命令: 
     javac -classpath "" CPTest2.java 
     雖然目錄還是剛才的目錄,CPTest1和CPTest2也在相同的包裡,這一次卻失敗了。
     錯誤資訊可能是這樣的: 
     CPTest2.java:7: cannot resolve symbol 
     symbol : class CPTest1 
     location: class com.web_tomorrow.CPTest2 
     CPTest1 cpt1 = new CPTest1(); ^ 
     這一次和上一次成功的情況之間的區別之一就是CPTest2中有對CPTest1的引用: 
     CPTest1 cpt1 = new CPTest1(); 
     這次發生了什麼呢?當編譯器遇到對CP1Test的引用時,它會認為CP1Test類與當前編譯的CP2Test類在同一個包裡。這個假定是正
確的,於是編譯器來尋找com.web_tomorrow.CP1Test,但它沒有地方可以找,因為我們已經把類搜尋路徑
明確的指定成了“”(也就是空)。 
     你可能認為,只要告訴編譯器在目前的目錄下尋找就可以解決這個問題。在Unix和Windows系統中,“目前的目錄”的標準符號都是一個點號(.),也就是這樣的命令: 
     javac -classpath "." CPTest2.java 
     Fail
Againt!跟剛才的例子完全一樣,現在的問題是雖然CPTest1.java在目前的目錄下,但它實現的類不是CPTest1,而是
com.web_tomorrow.CPTest1,編譯器將在目前的目錄下尋找目錄com/web_tomorrow。也就是說,它在目錄
[root]/com/web_tomorrow/com/web_tomorrow中尋找一個Java源檔案或類檔案,它們事實上根本不存在,於是出錯。 
     為了讓編譯器工作正常,我們不能讓類搜尋路徑指向包含CPTest1的目錄,而是按照Java標準中`package name = directory name'的規則,給類搜尋路徑指定編譯器能找到CPTest1的根目錄。這樣才能工作,雖然不太好看: 
     javac -classpath "../.." CPTest2.java 
     在考慮如何變得不難看之前,看一下這個例子(仍在同一目錄下) 
     javac -classpath "" CPTest1.java CPTest2.java 
     儘管classpath為空白,這次卻能工作。這是因為Java編譯器會在命令列中明確列出的所有原始碼中尋找引用。如果要編譯同一目錄下的多個類,有一種很簡單的方式: 
     javac -classpath "" *.java 
     '*.java'擴充為目前的目錄下所有.java檔案的列表。這就說明為什麼一次編譯多個檔案會成功,而同樣的檔案一個一個的編譯卻失敗。 
     編譯CPTest2有一個更方便的方法: 
     cd [root] 
     javac -classpath "." com/web_tomorrow/CPTest2.java 
     這次我們為CPtest2.java指定了完整的路徑,而在-classpath選項中使用了'.'。另外,我們沒有告訴編譯器在目前的目錄下尋找檔案,而是讓它從目前的目錄開始搜尋。
     因為我們要找的類是com.web_tomorrow.CPTest1,編譯器將在./com/web_tomorrow(也就是說目前的目錄下的com/web_tomorrow子目錄)下尋找。這才是CPTest1.java正確的位置。 
     事實上,儘管我在命令列只指定了CPTest2,但事實上CPTest1同時也會被編譯。編譯器在正確的位置找到這個.java檔案,但它不能判定這個.java檔案是否包含正確的類,所以它編譯這個檔案。但請注意如果是: 
     cd [root] 
     javac -classpath "." com/web_tomorrow/CPTest1.java 
     就不會導致CPTest2.java被編譯,因為編譯器編譯CPTest1時不需要知道CPTest2的任何事情。 
     類檔案與.java檔案分開的情況 
     到目前為止所有成功的例子都把輸出的.class檔案放到.java檔案同樣的位置,這是一種比較簡單的模式,應用非常廣泛。但很多開發人員喜歡
把源檔案和產生的檔案分開,因此它必須告訴編譯器為.class檔案維護不同的目錄。下面我們來看看這對類搜尋路徑有何影響。 
     首先我們刪除剛才幾個例子產生的所有.class檔案。我們還要有一個新的目錄來存放產生的.class檔案。命令列方式下的操作過程如下: 
     cd [root] 
     rm com/web_tomorrow/*.class 
     mkdir classes 
     如果你用Windows系統,別忘了把'/'替換成'\',現在的目錄結構如下:

     [root] com 
     web_tomorrow 
     CPTest1.java 
     CPTest2.java classes 
     現在編譯CPTest1.java,指定類檔案的目標目錄(通過-d選項) 
     cd [root] 
     javac -d classes -classpath "" com/web_tomorrow/CPTest1.java 
     成功,但你會注意到,.class檔案根本不是直接放在classes目錄中,而是有了一個新的目錄結構:

     [root] com 
     web_tomorrow 
     CPTest1.java 
     CPTest2.java 
     classes com 
     web_tomorrow 
     CPTest1.class 
     編譯器建立了一個符合包結構的目錄結構,這很有用,我們馬上就會看到。當開始編譯CPTest2.java時我們有兩個選擇,第一種是像上面一
樣,讓編譯器同時也編譯一次CPTest1;另一種是用-classpath選項指定剛才編譯好的.class檔案,這種方法更好一點,因為我們不必再來
編譯一遍CPTest1: 
     cd [root] 
     javac -d classes -classpath classes com/web_tomorrow/CPTest2.java      
     完成之後,我們的目錄結構如下:

     [root] com 
     web_tomorrow 
     CPTest1.java 
     CPTest2.java 
     classes 
     com 
     web_tomorrow 
     CPTest1.class 
     CPTest2.class 
     當然我們也可以在同一條命令中編譯兩個.java檔案,結果完全相同。
4.classpath中的JAR打包檔案 
     Java編譯器和運行環境不僅可以在獨立檔案中搜尋類,還可以在JAR打包檔案中進行尋找。一個JAR打包檔案可以維護它自己的目錄結
構,Java按照跟普通目錄結構完全相同的方式,`directory name = package
name',進行搜尋。由於JAR本身就是一個目錄,所以在類搜尋路徑中包含JAR打包檔案時,路徑必須引用JAR本身,而不是它所在的目錄。如果我在目
錄/myclasses下有一個JAR myclasses.jar,我需要指定:javac -classpath
/myclasses/myclasses.jar ...而不僅僅是目錄myclasses。
5.多個類搜尋目錄 
     在上面的例子中,每次我們都只讓javac在一個目錄下搜尋。實際工作中,你的類搜尋路徑會包含很多目錄和JAR檔案。Javac的-classpath選項允許指定多個位置,但請注意,它的具體文法在Unix系統和Windows系統中稍有區別。 
     在Unix系統中是: 
     javac -classpath dir1:dir2:dir3 ... 
     而Windows系統則是: 
     javac -classpath dir1;dir2;dir3 ... 
     這是因為Windows使用冒號(:)作為檔案名稱的一部分,因此不能作為檔案名稱分隔字元。當然目錄分隔字元也不一樣:Unix的正斜杠(/)和Windows的反斜線(\)。
6.系統classpath 
     除了在javac命令指定類搜尋路徑外,我們還可以使用'系統'類路徑。如果命令中沒有指定特定的路徑,Java編譯器和JVM都使用系統路徑。在Unix和Windows系統中,系統路徑都通過環境變數設定。

     例如在使用bash shell的Linux系統中:
     CLASSPATH=/myclasses/myclasses.jar;export CLASSPATH 
     在Windows中: 
     set CLASSPATH=c:\myclasses\myclasses.jar 
     這是暫時改變系統變數CLASSPATH的好方法,但如果你想這些變化一直保留下來,你就需要針對你的系統做一些修改。例如在Linux系統
中,我會將這個命令放到我目錄下的檔案.bashrc中。而在Windows 2000/NT中可以通過‘控制台’來修改。 
     如果你有很多隨時用到的JAR,設定系統變數CLASSPATH就顯得尤為重要。比如說,如果我在用Sun的J2EE參考實現開發EJB應用,
所有EJB相關的類都發布在一個叫做“j2ee.jar”的JAR打包檔案中,我希望這個JAR一直都在類搜尋路徑中。另外,還有很多人希望無論目前的目錄
是什麼,搜尋路徑始終包含目前的目錄。所以在我的.bashrc檔案中就有這樣一行: 
     CLASSPATH=/usr/j2ee/j2ee.jar:.;export CLASSPATH 
     其中`.'是指'目前的目錄'

     很容易看到命令列中的-classpath選項覆蓋預設的系統類別路徑;它
不是擴充而是覆蓋。因此如果我們既要包含預設的系統路徑,又要增加一些時怎麼處理呢?我們可以簡單的通過-classpath選項同時列出預設值和我們要
額外增加的部分。而一個更好的辦法是引用系統變數CLASSPATH。當然,這個文法對Windows系統和Unix系統是不同的。

     在Unix上: 
     javac -classpath $CLASSPATH:dir1:dir2 ... 
     其中$CLASSPATH擴充為系統變數CLASSPATH。在Windows上: 
     javac -classpath %CLASSPATH%;dir1:dir2 ...

  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.