Java的Package與Import機制之我的理解(初學者的心得)
來源:互聯網
上載者:User
初學|心得
以下內容的測試條件是你的機器上,設定了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。