Java 中的 ClassPath 和 Package

來源:互聯網
上載者:User

發信人:   SuperMMX   (笑天子),   信區:   Java 
標     題:   [整理]   Java   的   Class   Path   和   Package 
發信站:   BBS   水木清華站   (Mon   Apr     9   08:15:01   2001) 
                      Java   中的   ClassPath   和   Package 
前言: 
        由於這兩個問題新手問得較多,   且回答比較零散,   很難統一整理,   所 
以就直接寫了一篇,   還請大家見諒. 
本文: 
一,   類路徑   (class   path) 
        當你滿懷著希望安裝好了   java,   然後興沖沖地寫了個   hello   world,然後編譯, 
運行,   就等著那兩個美好的單詞出現在眼前,   可是不幸的是,   只看到了   Can 't   find 
class   HelloWorld   或者   Exception   in   thread   "main "   java.lang.NoSuchMethodError 
:   maain. 
為什麼呢?   編譯好的   class   明明在呀. 
        我們一起來看一看   java   程式的運行過程.   我們已經知道   java   是通過   java 
虛擬機器來解釋啟動並執行,   也就是通過   java   命令,   javac   編譯產生的   .class 
檔案就是虛擬機器要執行的代碼,   稱之為位元組碼(bytecode),   虛擬機器通過   classloader 
來裝載這些位元組碼,   也就是通常意義上的類.   這裡就有一個問題,   classloader   從 
哪裡知道   java   本身的類庫及使用者自己的類在什麼地方呢?   或者有著預設值(當前路徑) 

或者要有一個使用者指定的變數來表明,   這個變數就是類路徑(classpath),   或者在運行 
的時候傳參數給虛擬機器.   這也就是指明   classpath   的三個方法.   編譯的過程和運行 
的過程大同小異,   只是一個是找出來編譯,   另一個是找出來裝載. 
        實際上   java   虛擬機器是由   java   luncher   初始化的,   也就是   java   (或   java.exe) 
這個程式來做的.   虛擬機器按以下順序搜尋並裝載所有需要的類: 
        1,   引導類:   組成   java   平台的類,   包含   rt.jar   和   i18n.jar   中的類. 
        2,   擴充類:   使用   java   擴充機制的類,   都是位於擴充目錄($JAVA_HOME/jre/lib/e 
xt) 
中的   .jar   檔案包. 
        3,   使用者類:   開發人員定義的類或者沒有使用   java   擴充機制的第三方產品.   你必須在 

命令列中使用   -classpath   選項或者使用   CLASSPATH   環境變數來確定這些類的位置.   我 

們在上面所說的使用者自己的類就是特指這些類. 
        這樣,   一般來說,   使用者只需指定使用者類的位置,   引導類和擴充類是 "自動 "尋找的. 
        那麼到底該怎麼做呢?   使用者類路徑就是一些包含類檔案的目錄,   .jar,   .zip   檔案的 

列表,   至於類具體怎麼找,   因為牽扯到   package   的問題,   下面將會說到,   暫時可認為 
只要包含了這個類就算找到了這個類.   根據平台的不同分隔字元略有不同,   類   unix   的系 

統基本上都是   ": ",   windows   多是   "; ".   其可能的來源是: 
        *   ". ",   即目前的目錄,   這個是預設值. 
        *   CLASSPATH   環境變數,   一旦設定,   將預設值覆蓋. 
        *   命令列參數   -cp   或者   -classpath,   一旦指定,   將上兩者覆蓋. 
        *   由   -jar   參數指定的   .jar   檔案包,   就把所有其他的值覆蓋,   所有的類都來自這 
個指 
定的檔案包中.   由於產生可執行檔   .jar   檔案,   還需要其他一些知識,   比如   package,   
還有 
特定的設定檔,   本文的最後會提到.   可先看看   jdk   內建的一些例子. 
        我們舉個   HelloWorld   的例子來說明.   先做以下假設: 
        *   目前的目錄是   /HelloWorld   (或   c:\HelloWorld,   以後都使用前一個) 
        *   jdk   版本為   1.2.2   (linux   下的) 
        *   PATH   環境變數設定正確.   (這樣可以在任何目錄下都可以使用工具) 
        *   檔案是   HelloWorld.java,   內容是: 
        public   class   HelloWorld 
        { 
                public   static   void   main(String[]   args) 
                { 
                        System.out.println( "Hello   World!\n "); 
                        System.exit(0); 
                } 
        } 
        首先這個檔案一定要寫對,   如果對   c   熟悉的話,   很有可能寫成這樣: 
        public   static   void   main(int   argc,   String[]   argv) 
        { 
                .... 
        } 
        這樣是不對的,   不信可以試一試.   由於手頭沒有   java   的規範,   所以 
作如下猜想:   java   的   application   程式,   必須以   public   static   void   main(String[ 
]) 
開始,   其他不一樣的都不行. 
        到現在為止,   我們設定方面只設定了   PATH. 
        1,   當前路徑就是指你的   .class   檔案在目前的目錄下, 
              [HelloWorld]$   javac   HelloWorld.java     //這一步不會有多大問題, 
              [HelloWorld]$   java   HelloWorld                 //   這一步可能就會有問題. 
        如果出了象開頭那樣的問題,   首先確定不是由於敲錯命令而出錯.   如果沒有敲錯命 
令, 
那麼接著做: 
              [HelloWorld]$   echo   $CLASSPATH 
              或者 
              c:\HelloWorld> echo   %CLASSPATH% 
        看看   CLASSPATH   環境變數是否設定了,   如果設定了,   那麼用以下命令: 
              [HelloWorld]$   CLASSPATH= 
              或者 
              c:\HelloWorld>   set   CLASSPATH= 
        來使它為空白,   然後重新運行.   這次使用者類路徑預設的是   ". ",   所以應該不會有相 
同的問題了.   還有一個方法就是把   ". "   加入到   CLASSPATH   中. 
              [/]$   CLASSPATH=$CLASSPATH:. 
              或者 
              c:\HelloWorld>   set   CLASSPATH=%CLASSPATH%;. 
        同樣也可以成功.   Good   Luck. 
        2,   當你的程式需要第三方的類庫支援,   而且比較常用,   就可以採用此種方法.比如 
常 
用的資料庫驅動程式,   寫   servlet   需要的   servlet   包等等.   設定方法就是在環境變數 
中 
加入   CLASSPATH.   然後就可以直接編譯運行了.   還是以   HelloWorld   為例,   比如你想在 
根 
目錄中運行它,   那麼你直接在根目錄下執行 
            $   java   HelloWorld 
            或者 
            c:\> java   HelloWorld 
        這樣肯定會出錯,   如果你的   CLASSPATH   沒有改動的話.   我想大家應該知道為什麼錯 
了 
吧,   那麼怎麼改呢?   前面說過,   使用者類路徑就是一些包含你所需要的類的目錄,   .jar   檔 
案 
包,   .zip   包.   現在沒有產生包,   所以只好把   HelloWorld.class   所在的目錄加到   CLAS 
SPAT 
了,   根據前面的做法,   再運行一次,   看看,   呵呵,   成功了,   換個路徑,   又成功了!!   不僅 
僅?br   />   以直接運行其中的類,   當你要   import   其中的某些類時,   同樣處理. 
        不知道你想到沒有,   隨著你的系統的不斷的擴充,   (當然了,   都是一些需要   java   的 
東撾? 
如果都加到這個環境變數裡,   那這個變數會越來越臃腫,   雖然環境變數空間可以開很大 
,   總 
覺得有些不舒服.   看看下面一個方法. 
        3,   在命令列參數中指明   classpath. 
        還是和上面相同的目標,   在任何目錄下執行   HelloWorld,   用這個方法怎麼實現呢? 

            [/]$   java   -cp   /HelloWorld   HelloWorld 
            或者 
            c:\> java   -cp   c:\HelloWorld   HelloWorld 
        就可以了.   這是這種方法的最簡單的應用了.   當你使用了另外的包的時候,   還可以 
採用用?br   />   種方法.   例如: 
            $   javac   -classpath   aPath/aPackage.jar:.   myJava.java 
            $   java     -cp   aPath/aPackage.jar:.   myJava 
            或者 
            c:\>   javac   -classpath   aPath\aPackage.jar;.   myJava.java 
            c:\>   java     -cp   aPath\aPackage.jar;.   myJava 
        這種方法也有一個不方便的的地方就是當第三方包所在的路徑較長或者需要兩個以 
上包包?br   />   時候,   每次編譯運行都要寫很長,   非常不方便,   這時候可以寫指令碼來解決.   比 
如一個例子: 
            compile       (檔案,   許可權改為可執行,   目前的目錄) 
            $   cat   compile 
--------------------------- 
            #!/bin/bash 
            javac   -classpath   aPath\aPackage.jar:anotherPath\anotherPackage.jar:.   m 
yJavva.java 
--------------------------- 
            run   (檔案,   許可權改為可執行,   目前的目錄) 
            $cat   run 
--------------------------- 
            #!/bin/bash 
            java   -cp   aPath\aPackage.jar:anotherPath\anotherPackage.jar:.   myJava 
--------------------------- 
            或者: 
            compile.bat 
            c:\HelloWorld>   type   compile.bat 
------------------------- 
            javac   -classpath   aPath\aPackage.jar:anotherPath\anotherPackage.jar:.   m 
yJavva.java 
------------------------- 
            run.bat 
            c:\HelloWorld>   type   run.bat 
------------------------ 
            java   -cp   aPath\aPackage.jar:anotherPath\anotherPackage.jar:.   myJava 
------------------------ 
        就可以了.   試試看. 
前面提到了擴充類,   擴充類是什麼呢?   java   的擴充類就是應用程式開發人員用來 
擴充核心平台功能的   java   類的包(或者是   native   code).   虛擬機器能像使用系統類別一 
樣使用這些擴充類.   有人建議可以把包放入擴充目錄裡,   這樣,   CLASSPATH   也不用設了 

也不用指定了,   豈不是很方便?   確實可以正確運行,   但是個人認為這樣不好,   不能什麼 

東西都往裡擱,   一些標準的擴充包可以,   比如,   JavaServlet,   Java3D   等等.   可以提個 

建議,   加一個環境變數,   比如叫   JARPATH,   指定一個目錄,   專門存放使用者的   jar   zip 
等包,   這個要等   SUN   公司來做了. 
        windows98   下,   我原來安裝的時候,   一直裝不上,   總是死機,   好不容易裝上了,   缺 

省的是不能運行正確的,   然後把   tool.jar   放入   CLASSPATH   後工作正常.   現在作測試, 

去掉仍然是正確的.   經過多次測試,   發現如果原來曾裝過   jdk   的都很好,   沒有裝過的 
裝的時候會死機,   多裝幾次就可以了.   如果你發現正確安裝後,   不能正常工作,   就把 

tools.jar   加入   CLASSPATH,   試一下. 

二,   包   (package) 
        Java   中的   "包 "   是一個比較重要的概念,   package   是這樣定義的: 
Definition:   A   package   is   a   collection   of   related   classes   and   interfaces 
that   provides   access   protection   and   namespace   management. 
        也就是:   一個包就是一些提供訪問保護和命名空間管理的相關類與介面的集合. 
        使用包的目的就是使類容易尋找使用,   防止命名衝突,   以及控制訪問. 
        這裡我們不討論關於包的過多的東西,   只討論和編譯,   運行,   類路徑相關的東西. 
至於包的其他內容,   請自己查閱相關文檔. 
        簡單一點來說,   包就是一個目錄,   下面的子包就是子目錄,   這個包裡的類就是 
這個目錄下的檔案.   我們用一個例子來說明. 
        首先建目錄結構如下:   PackageTest/source/,   以後根目錄指的是   PackageTest 
目錄,   我們的來源程式放在   source   目錄下.   來源程式如下: 
PackageTest.java 
package   pktest; 
import   pktest.subpk.*; 
public   class   PackageTest 

        private   String   value; 
        public   PackageTest(String   s) 
        { 
                value   =   s; 
        } 
        public   void   printValue() 
        { 
                System.out.println( "Value   of   PackageTest   is   "   +   value); 
        } 
        public   static   void   main(String[]   args) 
        { 
                PackageTest   test   =   new   PackageTest( "This   is   a   Test   Package "); 
                test.printValue(); 
                PackageSecond   second   =   new   PackageSecond( "I   am   in   PackageTest "); 
                second.printValue(); 
                PackageSub   sub   =   new   PackageSub( "I   am   in   PackageTest "); 
                sub.printValue(); 
                System.exit(0); 
        } 

PackageSecond.java 
package   pktest; 
public   class   PackageSecond 

        private   String   value; 
        public   PackageSecond(String   s) 
        { 
                value   =   s; 
        } 
        public   void   printValue() 
        { 
                System.out.println( "Value   of   PackageSecond   is   "   +   value); 
        } 

PackageSub.java 
package   pktest.subpk; 
import   pktest.*; 
public   class   PackageSub 

        private   String   value; 
        public   PackageSub(String   s) 
        { 
                value   =   s; 
        } 
        public   void   printValue() 
        { 
                PackageSecond   second   =   new   PackageSecond( "I   am   in   subpackage. "); 
                second.printValue(); 
                System.out.println( "Value   of   PackageSub   is   "   +   value); 
        } 

Main.java 
import   pktest.*; 
import   pktest.subpk.*; 
public   class   Main() 

        public   static   void   main() 
        { 
                PackageSecond   second   =   new   PackageSecond( "I   am   in   Main "); 
                second.printValue(); 
                PackageSub   sub   =   new   PackageSub( "I   am   in   Main "); 
                sub.printValue(); 
                System.exit(0); 
        } 

        其中,   Main.java   是包之外的一個程式,   用來測試包外的程式訪問包內的類, 
PackageTest.java   屬於   pktest   這個包,   也是主程式.   PackageSecond.java   也 
屬於   pktest,   PackageSub   屬於   pktest   下的   subpk   包,   也就是   pktest.subpk. 
詳細使用方式,   請參看來源程式. 
        好了,   先把來源程式都放在   source   目錄下,   使   source   成為目前的目錄,   然後編 
譯一下,   呵呵,   出錯了, 
Main.java:1:   Package   pktest   not   found   in   import. 
import   pktest.*; 
        這裡涉及到類路徑中包是怎麼尋找的,   前面我們做了一點假設:   "只要包含了 
這個類就算找到了這個類 ",   現在就有問題了.   其實   jdk   的   工具   javac   java 
javadoc   都需要尋找類,   看見目錄,   就認為是包的名字,   對於   import   語句來說, 
一個包對應一個目錄.   這個例子中,   import   pktest.*,   我們知道類路徑可以包 
含一個目錄,   那麼就以那個目錄為根,   比如有個目錄   /myclass,   那麼就會在尋找 
/myclass/pktest   目錄及其下的類.   所有的都找遍,   如果沒有就會報錯.   由於現在 
的類路徑只有目前的目錄,   而目前的目錄下沒有   pktest   目錄,   所以就會出錯.   類路徑 
還可以包含   .jar   .zip   檔案,   這些就是可以帶目錄的壓縮包,   可以把   .jar   .zip 
檔案看做一個虛擬目錄,   然後就和目錄一樣對待了. 
        好了,   應該知道怎麼做了吧,   修改後的目錄結構如下: 
        PackageTest 
        | 
        |__source       Main.java 
              | 
              |__pktest       PackageTest.java       PackageSecond.java 
                    | 
                    |__subpk     PackageSub.java 
        然後重新編譯,   運行,   哈哈,   通過了.   我們再來運行一下   PackageTest. 
        [source]$   java   pktest/PackageTest 
        怎麼又出錯了? 
Exception   in   thread   "main "   java.lang.NoClassDefFoundError:   pktest/PackageTes 

        是這樣的,   java   所要啟動並執行是一個類的名字,   它可不管你的類在什麼地方,   就象 
我們前面所討論的一樣來尋找這個類,   所以它把   pktest/PackageTest   看成是一個類的 

名字了,   當然會出錯了,   應該這麼做, 
        [source]$   java   pktest.PackageTest 
        大家應該明白道理吧,   我就不多說了.   注意   javac   不一樣,   是可以指明源檔案路徑 

的,   javac   只編譯,   不運行,   尋找類也只有在源檔案中碰到   import   時才會做,   與源文 
件 
所在的包沒有關係. 
        似乎還又些不好的地方,   怎麼產生的   .class   檔案這麼分散呀,   看著真彆扭.   別急 

javac   有一個   -d   命令列參數,   可以指定一個目錄,   把產生的   .class   檔案按照包給你 

好好地擱在這個目錄裡面. 
        [source]$   mkdir   classes 
        [source]$   javac   -d   classes   pktest/PackageTest.java 
        [source]$   javac   -d   classes   Main.java 
        那麼運行怎麼運行呢? 
        [source]$   cd   classes 
        [classes]$   java   pktest.PackageTest 
        [classes]$   java   Main 
        就可以了.   其實   jdk   的這一套工具小巧簡單,   功能強大,   不會用或者用錯其 
實不關工具的事,   關鍵是明白工具背後的一些原理和必要的知識.   Integration Environment是很好, 
但是它屏蔽了很多底層的知識,   不出錯還好,   一旦出錯,   如果沒有這些必要的知識 
就很難辦,   只好上   bbs   問,   別人只告訴了你解決的具體方法,   下一次遇到稍微變化 
一點的問題又不懂了.   所以不要拘泥於工具,   java   的這一套工具組合起來使用,   中 
小型工程(五六十個類),   還是應付得下來的. 
三,   jar   檔案 
        以下把   .jar   .zip   都看做是   .jar   檔案. 
1,     從前面我們可以看出來   jar   檔案在   java   中非常重要,   極大地方便了使用者的 
使用.   我們也可以做自己的   .jar   包. 
        還是使用前面那個例子,   Main.java   是包之外的東西,   用了   pktest   包中的類, 
我們現在就是要把   pktest   做成一個   .jar   包,   很簡單,   剛才我們已經把   pktest 
中的   .class   都集中起來了, 
        [classes]$   jar   -cvf   mypackage.jar   pktest 
        就會產生   mypackage.jar   檔案,   測試一下,   剛才我們產生的   Main.class   就在 
        classes   目錄下,   所以,   從前面可以知道: 
        [classes]$   java   -cp   mypackage.jar:.   Main 
        就可以運行了. 
2,     如果你看過   jdk   所帶的例子,   你就會知道,   .jar   還可以直接運行, 
        [/demo]$   java   -jar   aJar.jar 
        那好,   就那我們的試一試, 
        [classes]$   java   -jar   mypackage.jar 
        Failed   to   load   Main-Class   manifest   attribute   from 
        mypackage.jar 
        看來我們的   jar   和它的   jar   還不一樣,   有什麼不一樣呢?   拿它一個例子出來, 
重新編譯,   產生   .jar   檔案,   比較後發現,   是   .jar   壓縮包中   META-INF/MANIFEST.MF 
檔案不一樣,   多了一行,   Main-Class:   xxxxx,   再看看出錯資訊,   原來是沒有指定 
Main-Class,   看看   jar   命令,   發現有一個參數   -m, 
        -m     include   manifest   information   from   specified   manifest   file 
        和出錯資訊有點關係,   看來它要讀一個配製檔案.   只好照貓畫虎寫一個了. 
        [classes]$   cat   myManifest 
        Manifest-Version:   1.0 
        Main-Class:   pktest.PackageTest 
        Created-By:   1.2.2   (Sun   Microsystems   Inc.) 
        [classes]$   jar   cvfm   mypackage.jar   myManifest   pktest 
        added   manifest 
        adding:   pktest/(in   =   0)   (out=   0)(stored   0%) 
        adding:   pktest/PackageSecond.class(in   =   659)   (out=   395)(deflated   40%) 
        adding:   pktest/subpk/(in   =   0)   (out=   0)(stored   0%) 
        adding:   pktest/subpk/PackageSub.class(in   =   744)   (out=   454)(deflated   38%) 

        adding:   pktest/PackageTest.class(in   =   1041)   (out=   602)(deflated   42%) 
        [classes]$   java   -jar   mypackage.jar 
        Value   of   PackageTest   is   This   is   a   Test   Package 
        Value   of   PackageSecond   is   I   am   in   PackageTest 
        Value   of   PackageSecond   is   I   am   in   subpackage. 
        Value   of   PackageSub   is   I   am   in   PackageTest 
        好了,   成功了,   這樣就做好了一個可以直接執行的   .jar   檔案.   大家可以自己試一 
試 
做一個以   Main   為主程式的可執行檔   jar. 


小結: 
        這篇文章中,   我們討論了   java   中的   class   path,   package,   jar   等基本但比較 
重要的東西,   主要是   class   path.   並不是簡單的一份   CLASSPATH   的完全功略,   而是 
試圖讓讀者明白其原理,   自己思考,   自己動手.   其實大多數東西都在   sun   的   java   doc 

中都有,   我只不過結合例子稍微談了一下,   希望能有所協助.   由於條件所限,   只測試了 

jdk1.2.2   在   98   及   linux   的情況,   其他版本的   jdk   和平台請大家自己測試,   錯誤在 
所難免,   還請指正. 
        下面是一些需要注意的問題: 
1,   如果類路徑中需要用到   .jar   檔案,   必須把   jar   檔案的檔案名稱放入類路徑,   而不是 

其所在的目錄. 
2,   在任何時候,   類名必須帶有完全的包名, 
3,   ". "   目前的目錄最好在你的類路徑中. 
        下面是一些常見的編譯和啟動並執行模式. 
4.   To   compile   HelloWorld.java   app   in   the   default   package   in   C:\MyDir,   use 
                CD   \MyDir 
                        C:\jdk1.3\bin\Javac.exe   -classpath   .   HelloWorld.java 
5.   To   run   a   HelloWorld.class   app,   in   the   default   package   in   C:\MyDir,   use 
                CD   \MyDir 
                        C:\jdk1.3\bin\Java.exe   -classpath   .   HelloWorld 
6.   To   run   a   HelloWorld.class   app,   in   the   default   package   in   a   jar   in   C:\MyDi 
r,   uuse 
                CD   \MyDir 
                        C:\jdk1.3\bin\Java.exe   -classpath   HelloWorld.jar   HelloWorld 
7.   To   compile   a   HelloWorld.java   app   in   C:\MyPackage,   in   package   MyPackage,   u 
se 
                CD   \ 
                        C:\jdk1.3\bin\Javac.exe   -classpath   .   MyPackage\HelloWorld.java 
8.   To   run   a   HelloWorld.class   app   in   C:\MyPackage,   in   package   MyPackage,   use 
                CD   \ 
                        C:\jdk1.3\bin\Java.exe   -classpath   .   MyPackage.HelloWorld 
9.   To   run   a   HelloWorld.class   app   in   C:\MyPackage,   in   a   jar   in   package   MyPack 
age,,   use 
                CD   \MyDir 
                        C:\jdk1.3\bin\Java.exe   -classpath   HelloWorld.jar   MyPackage.Hello 
Worl 
(注:   default   package   指的是在程式中不指定任何包). 
        最後一個小小的建議,   把   sun   的   jdk   tools   documentation   好好地看一看, 
把   jdk   的那些工具   java   javac   javadoc   jar   javap   jdb......好好用一用,   會 
有好處的.   The   Simplest   Is   The   Best. 
參考文獻: 
Java   Tools   Documentation. 
Java   Glossary   http://mindprod.com/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.