<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Java 中的 ClassPath 和 Package


    正文:

    一, 類路徑 (class path)

        當你滿懷著希望安裝好了 java, 然后興沖沖地寫了個 hello world,然后編譯,
    運行, 就等著那兩個美好的單詞出現在眼前, 可是不幸的是, 只看到了 Can't find
    class HelloWorld 或者 Exception in thread "main" java.lang.NoSuchMethodError: main.
    為什么呢? 編譯好的 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/ext)
    中的 .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 所在的目錄加到 CLASSPATH
    了, 根據前面的做法, 再運行一次, 看看, 呵呵, 成功了, 換個路徑, 又成功了!! 不僅僅可
    以直接運行其中的類, 當你要 import 其中的某些類時, 同樣處理.
        不知道你想到沒有, 隨著你的系統的不斷的擴充, (當然了, 都是一些需要 java 的東西)
    如果都加到這個環境變量里, 那這個變量會越來越臃腫, 雖然環境變量空間可以開很大, 總
    覺得有些不舒服. 看看下面一個方法.
       
        3, 在命令行參數中指明 classpath.

        還是和上面相同的目標, 在任何目錄下執行 HelloWorld, 用這個方法怎么實現呢?

          [/]$ java -cp /HelloWorld HelloWorld
          或者
          c:\>java -cp c:\HelloWorld HelloWorld

        就可以了. 這是這種方法的最簡單的應用了. 當你使用了另外的包的時候, 還可以采用這
    種方法. 例如:

          $ 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

        這種方法也有一個不方便的的地方就是當第三方包所在的路徑較長或者需要兩個以上包的
    時候, 每次編譯運行都要寫很長, 非常不方便, 這時候可以寫腳本來解決. 比如一個例子:

          compile   (文件, 權限改為可執行, 當前目錄)

          $ cat compile
    ---------------------------
          #!/bin/bash

          javac -classpath aPath\aPackage.jar:anotherPath\anotherPackage.jar:. myJava.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:. myJava.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/PackageTest

        是這樣的, 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 的這一套工具小巧簡單, 功能強大, 不會用或者用錯其
    實不關工具的事, 關鍵是明白工具背后的一些原理和必要的知識. 集成環境是很好,
    但是它屏蔽了很多底層的知識, 不出錯還好, 一旦出錯, 如果沒有這些必要的知識
    就很難辦, 只好上 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:\MyDir, use
            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, use
            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 MyPackage, use
            CD \MyDir
                C:\jdk1.3\bin\Java.exe -classpath HelloWorld.jar MyPackage.HelloWorld

    (注: 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/

    posted on 2007-10-26 10:25 湘江夜游神 閱讀(217) 評論(0)  編輯  收藏 所屬分類: JAVA生活

    Locations of visitors to this page

    主站蜘蛛池模板: 亚洲va无码专区国产乱码| 国产亚洲成av片在线观看| 狠狠色香婷婷久久亚洲精品| 67pao强力打造高清免费| 777亚洲精品乱码久久久久久| 日本免费中文视频| 亚洲人成图片小说网站| 伊人免费在线观看高清版| 亚洲人成网77777亚洲色| 特级无码毛片免费视频尤物| 精品亚洲成a人片在线观看| 日日麻批免费40分钟日本的| 亚洲一级在线观看| 四虎影视大全免费入口| 在线播放亚洲精品| 久久久亚洲精品蜜桃臀| 久久久久免费精品国产| 亚洲欧洲精品久久| 成人免费网站在线观看| 一级做a爱过程免费视| 亚洲免费网站观看视频| 看全免费的一级毛片| 亚洲一区二区三区无码中文字幕| 免费国产成人午夜在线观看| 亚洲白色白色在线播放| 免费观看男人免费桶女人视频| 深夜A级毛片视频免费| 国产亚洲免费的视频看| 中文字幕影片免费在线观看| 免费无码专区毛片高潮喷水 | 国产在线播放免费| 成年网站免费入口在线观看| 亚洲国产天堂在线观看| 天天干在线免费视频| 精品久久久久久国产免费了| 亚洲男女性高爱潮网站| 亚洲国产精品13p| 久久不见久久见免费视频7| 亚洲精品无码久久久影院相关影片| 91香蕉国产线在线观看免费 | 国产亚洲成在线播放va|