Java的Package與Import機制之我的理解(初學者的心得) - 中國WEB開發者網絡 (http://www.webasp.net) -- 技術教程 (http://www.webasp.net/article/) --- Java的Package與Import機制之我的理解(初學者的心得) (http://www.webasp.net/article/23/22119.htm) |
| -- 作者:未知 -- 發佈日期: 2005-04-30 |
以下內容的測試條件是你的機器上,設置了path命令PATH= D:\JDK1.4\BIN;D:\JDK1.4\LIB;,可以正常執行java和javac命令,不用設置classpath路徑的情況下。 從一個簡單的例子談談package與import機制 基本原則:為什麼需要將Java文件和類文件切實安置到其所歸屬之Package所對應的相對路徑下。 為什麼要這樣做呢?如果你在程序中,用到打包命令package,並且直接編譯和執行該程序。例如:以下面程序為例: package a.b.c; public class hello { public static void main(String args[]) { System.out.println("Hello the world!"); } } 此程序可以編譯通過,但是執行時,卻提示以下錯誤! D:\my\xdj>javac hello.java D:\my\xdj>java hello Exception in thread "main" java.lang.NoClassDefFoundError: hello (wrong name: a/ b/c/hello) at java.lang.ClassLoader.defineClass0(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:537) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:12 3) at java.net.URLClassLoader.defineClass(URLClassLoader.java:251) at java.net.URLClassLoader.access$100(URLClassLoader.java:55) at java.net.URLClassLoader$1.run(URLClassLoader.java:194) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:187) at java.lang.ClassLoader.loadClass(ClassLoader.java:289) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:274) at java.lang.ClassLoader.loadClass(ClassLoader.java:235) at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:302) D:\my\xdj> 在xdj目錄下建立一個\a\b\c子目錄把hello.java放在它下面,用以下命令進行編譯和執行時,可正常通過! D:\my\xdj>javac d:\my\xdj\a\b\c\hello.java D:\my\xdj>java a.b.c.hello Hello the world! D:\my\xdj> 再看下面另外一種情況,先讓我們在xdj目錄下建立兩個文件a.java和b.java文件,其內容如下。 a.java文件內容: import a.b.c.*; public class a { public static void main(String[] args) { b b1=new b(); b1.print(); } } a.java文件內容: package a.b.c; public class b { public void print() { System.out.println("我是被調用子類的程序輸出呀!"); } } 直接編譯a.java文件時,會提示以下錯誤! D:\my\xdj>javac a.java a.java:1: package a.b.c does not exist import a.b.c.*; ^ a.java:6: cannot access b bad class file: .\b.java file does not contain class b Please remove or make sure it appears in the correct subdirectory of the classpa th. b b1=new b(); ^ 2 errors D:\my\xdj> 接下來,我們把b.java移到xdj\a\b\c\下,並把\xdj目錄下的b.java刪除掉呀!重新執行編譯指令,這次肯定可以編譯成功!你可以發現b.java也同時被編譯過了,這就是所謂的make編譯方式。 D:\my\xdj>javac a.java D:\my\xdj> 提示1:如果你在\xdj目錄下仍保留一個b.java文件的話,執行對主程序的編譯命令時仍會報錯!你自己可以試試呀! 提示2:如果你刪除\xdj\a\b\c\b.java文件的話,保留b.class文件,執行對主程序的編譯命令時是可以通過,此地可以不需要子程序的源代碼。 提出一個問題:如果把目錄\a\b\c全部剪切到其它目錄,如D盤根目錄下,在\xdj目錄如果執行編譯和執行命令呢? 很明顯,會報以下錯誤!當然了,前提條件是你沒有設置classpath路徑,其實只要沒把類搜索路徑設置到我這個位置就會出錯的!你試試吧! D:\my\xdj>javac a.java a.java:1: package a.b.c does not exist import a.b.c.*; ^ a.java:6: cannot resolve symbol symbol : class b location: class a b b1=new b(); ^ a.java:6: cannot resolve symbol symbol : class b location: class a b b1=new b(); ^ 3 errors D:\my\xdj>java a Exception in thread "main" java.lang.NoClassDefFoundError: a/b/c/b at a.main(a.java:6) D:\my\xdj> 解決的辦法可以用以下命令即可正常編譯和執行: D:\my\xdj>javac -classpath d:\ a.java D:\my\xdj>java -classpath d:\;a 我是被調用子類的程序輸出呀! D:\my\xdj> 提示3:-classpath參數,缺省是以當前目錄為根基目錄的,即不帶-classpath參數的情況下。 提示4:使用java.exe還是javac.exe,最好明確指定-classpath選項,可設置環境變量CLASSPATH即可,同時設置了-classpath參數和環境變量classpath時,會以-classpath參數為主的。如果在它們所指定的路徑或JAR文件中存有package名稱和類名稱相同的類,會引起混淆的! 如果你在D盤的根目錄生成一個打包文件a.zip,其內容目錄a\b\c\下的所有文件的話,你也可以用下面命令進行編譯和執行。 D:\my\xdj>javac -classpath d:\a.zip a.java D:\my\xdj>java -classpath d:\a.zip;. a 我是被調用子類的程序輸出呀! D:\my\xdj> 以上討論就暫告一段落吧!如果你還想進一步瞭解package與import機制話,哪你可以繼續往下看下去的。 深入分析package與import機制部分 不管你有沒有使用import指令,存在目前目錄下的類都會被編譯器優先採用,只要它不屬於任何package。這是因為編譯器總是先假設您所輸入的類名就是該類的全名(不屬於任何package),然後-classpath所指定的路徑中搜索屬於該類的.java文件或.class文件,在這裡可以知道default package的角色非常特殊。 必須明確告訴編譯器我們用到哪個package下的類,導入時或在包名稱.類名稱中進行引用。導入某個包時,一定要進行-classpath路徑指定某個包的位置。你如果指定了多個路徑話,如果在一個路徑下已經找到了該包話,就優先引用該包的類。 當java編譯器開始編譯某個類的源代碼時,首先它會做一件事情,這就是建立“類路徑引用表”,它是根據參數-classpath或classpath環境變量來建立的。如果沒有指定選項-classpath或環境變量CLASSPATH時,缺省情況下類路徑引用表只有一筆記錄,即當前的目錄(“.”)。環境變量CLASSPATH的內容會被選項-classpath所覆蓋,沒有累加效果。 當編譯器將類路徑引用表建立好之後,接著編譯器要確定它可以利用類引用表裡的數據作為相對起始路徑,找到所有用到的package。 編譯器還要完成一張名為“類引用表”與“相對類引用表”的數據結構。 整個編譯程序中package與import機制相關的部分流程如下: 開始, 建立類路徑引用表與類引用表; 如果建立成功:則類名稱解析程序: 如果已存在該類的類文件,繼續其它的編譯工作。 如果該類的文件不存在,尋找該類的源代碼文件: 如果找到,則編譯該 類的源代碼,繼續 其它的編譯工作。 此時,也可返回到 開始,make機制, 遞歸式編譯。 如果找不到,編譯 結束,發出警告 信息。 如果建立失敗:編譯結束,發出警告信息(2)。 JAVA動態鏈接本質研究 不管你在同一個源代碼(.java)中使用了幾個類聲明,它們都會一一編譯成.class文件,即使是內部類、匿名類都是一樣。在java中,對於每一個類所構成的類文件,都可將它視為動態鏈接庫。 在類文件中,所有對於特寫類的操作都被轉換成類的全名。Import除了用來指引編譯器解析出正確的類名稱之外,沒有其它功能。 在運行時期,仍然用到一個與編譯器相同的程序,就是類路徑引用表的建立,而利用動態鏈接載入類文件的機制流程如下: 開始, 建立類路徑引用表, 根據類文件內部的信息,與類路徑引用表的數據合成類文件的絕對路徑。 如果找到類文件,檢查該類的類文件內部信息,是否符合相對路徑信息: 如果符合,載入該類。 如果不符合,執行錯誤,發出 Exception信息。 如果找不到類文件,則執行錯誤,發出Exception信息。 最後,需要說明的是,在java中提供許多的類包,java語言中將完成與計算機底層相關的輸入輸出、常用的數據類型轉換等功能的函數封裝在包中。如果你的程序提示找不到這樣基礎包的話,你就可以用參數-classpath或環境變量classpath進行指定位置來解決此類問題! 後面寫的有點亂呀!不知道你能否理解呀!不過相信你看完我對package與import的討論後,應該會有所收穫的。如果你還有任何關於package與import的疑問的話,可以和我一起討論和交流呀!我的郵箱是:xiaodajin@sina.com。 |
| webasp.net |