以下內容的測試條件是你的機器上,設置了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的疑問的話,可以和我一起討論和交流呀!我的郵箱是:[email protected]。