`
talentkep
  • 浏览: 100203 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

JAVA package load 原理

阅读更多

第五章
package 與import 機制
所謂的定律,也不過是目前尚未被人推翻的假設罷了。
█前言
許多對 Java 程式設計有興趣的初學者,多半到書店買一本Java 程式設計
的入門書開始他的Java 程式設計之旅。首先, 他可能會到
http://www.javasoft.com 下載最新版本的Java 2 SDK,將它安裝在自己的電
腦上,然後依照書本的指示,用文字編輯器輸入一個名為HelloWorld.java 的
Java 應用程式原始碼檔。熟悉Java 的朋友當然可以順利通過編譯並成功地執
行Java 應用程式。但是對初學者來說,當他鍵入:
javac HelloWorld.java
之後可能會編譯成功,產生 HelloWorld.class,但是,接下來當他輸入:
java HelloWorld
時,卻可能發現 java.exe 跑出惱人的訊息:
Exception in thread "main" java.lang.NoClassDefFoundError:HelloWorld
如果是一位對命令列模式(console)沒有太多概念的初學者,可能連編譯這
一步都無法跨過,編譯時螢幕上會出現一大堆讓人不知如何下手的的錯誤訊息。
因為這個進入 Java 程式設計領域的重大門檻,不知澆熄了多少Java 初學
者的熱情:連簡單一個Hello World 的Java 程式,想看到其執行結果都不是一
件簡單的事情了,想當然書本裡頭五花八門的程式設計議題都不再有意義。這兩
個門檻不知道阻礙了多少人學習Java 程式設計。
本章上半部分將假設您是一位第一次使用 Java 的朋友,所以內容的編排都
是為了要打破這進入門檻,讓大家可以快快樂樂地學習Java 程式設計;而下半
部分就稍微複雜了點,如果您希望深入了解運作細節,那麼您可以繼續看完後半
部分,沒興趣的話略過也無妨。如果您曾試著使用JBuilder 這種威力強大的開
發工具,您一定也會發現如果對於Java 的package 與import 機制沒有妥善
的瞭解,那麼開發程式的時候一大堆的錯誤訊息一樣也會搞的您心煩意亂,希望
看完本篇之後,可以讓您更輕鬆駕馭像JBuilder 這種高階開發工具。
█初探package 與import 機制
進入 Java 程式設計領域的第一個門檻,就是要了解Java 的package 與
import 機制,每一位學習Java 的人都必須對這個機制有切確的了解,底下我
們將假設您剛裝好Java 2 SDK 於d:\jdk1.3.0_01 目錄之中(注意! package 與
import 機制不會因為您用了新版的JDK 而改變,如果您用的是JDK 1.4.x,甚
至是以後更新版本的Java 2 SDK,都會產生與本章相同的結果),並沒有修改任
何的環境變數。接著我們在D 磁碟機根目錄底下新增一個名為my 的空目錄
(d:\my),並以這個空目錄作為我們討論的起始點。
討論一:
首先,請您將目錄切換到 d:\my 底下,請先試著在命令列中輸入
java 或 javac
您的螢幕上可能會輸出錯誤訊息如下:
這是因為系統不知道到哪裡去找 java.exe 或javac.exe 的緣故。所以您可以試
著輸入指令:
path d:\jdk1.3.0_01\bin
然後您再重新輸入
java 或 javac
就會看到以下畫面:
請注意,以上說明只是為了強調path 環境變數的使用。事實上,幾乎大多
數版本的JDK 都會於安裝時主動在<Windows 安裝目錄>\system32 底下複製
一份java.exe,而<Windows 安裝目錄>\system32 通常又是Windows 預設
path 環境變數中的其中一個路徑,所以一般的情況下,都會發生可以執行
java.exe,卻不能執行javac.exe 的情形。這小小的差異會產生微妙的差別,
如果您閱讀過本書第一章,他們之間的差異對您來說已經非常清楚。
接下來,請您在 d:\my 目錄下新增兩個檔案,分別是:
A.java
public class A
{
public static void main(String[] args)
{
B b1=new B() ;
b1.print() ;
}
}
B.java
public class B
{
public void print()
{
System.out.println("package test") ;
}
}
接著請您在命令列輸入:
javac A.java
如果您的程式輸入無誤,那麼您就會在d:\my 中看到產生了兩個類別檔,分別
是A.class 與B.class。
請注意,javac.exe 是我們所謂的Java 編譯器,它具有類似make 的能力。
舉例來說,我們的A.java 中用到了B 這個類別,所以編譯器會自動幫您編譯
B.java。反過來說,如果您輸入的指令是javac B.java,由於B.java 中並沒有用
到類別A,所以編譯器並不會自動幫您編譯A.java,
編譯成功之後,請輸入指令:
java A
您會看螢幕上成功地輸出
package test
在此推薦您一個非常好用的選項: -verbose 。您可以在javac.exe 或java.exe
中使用此選項。他可以讓您更了解編譯和執行過程中JVM 所做的每件事情。如
果您在編譯的時候使用–verbose 選項,結果如下:
從這個選項,您可以發現,由於編譯器採用了 JDK 所在目錄底下那套JRE 的緣
故,所以編譯時使用<JRE 所在目錄>\lib\rt.jar 裡頭已經編譯好的核心類別函式
庫(第一章的時候我們提過Java 編譯器也是用Java 寫成的,您可以試著讓
javac.exe 使用其他JRE 所在路徑之下的rt.jar 來進行編譯嗎? 留給大家做個小
測驗)。
當您執行 Java 程式時,使用–verbose 選項的結果如下:
省略…
您可以發現,由於java.exe 根據預設的邏輯而選擇使用了JDK 所在目錄底下那
套JRE 來執行程式,所以執行時所載入的核心類別都是使用<JRE 所在目錄
>\lib\rt.jar 裡頭那些。
您一定覺得上述討論一的內容很簡單,您也可以做到,所以接下來我們要更
進一步,探究更複雜的機制。
討論二:
請先刪除討論一中所產生的類別檔,接著修改您的兩個原始碼,使其內容為:
A.java
import edu.nctu.* ;
public class A
{
public static void main(String[] args)
{
B b1=new B() ;
b1.print() ;
}
}
B.java
package edu.nctu ;
public class B
{
public void print()
{
System.out.println("package test") ;
}
}
接著請您在命令列輸入:
javac A.java
如果您的程式輸入無誤,那麼您會看到螢幕上出現了許多錯誤訊息,主要的錯誤
在於
A.java:1: package edu.nctu does not exist
意思是說找不到 edu.nctu 這個package。其他兩個錯誤都是因為第一個錯誤
所衍生。
可是不對呀,按照Java 的語法,既然B 類別屬於edu.nctu,所以我們在
B.java 之中一開始就使用
package edu.nctu ;
而A 類別用到了B 類別,所以我們也在A.java 開頭加上了
import edu.nctu.* ;
所以這段程式在理論上應該不會發生編譯錯誤才是。
為了解決這個問題,請您在 d:\my 目錄下建立一個名為edu 的目錄,在
edu 目錄下再建立一個名為nctu 的子目錄, 然後將B.java 移至
d:\my\edu\nctu 目錄下,請重新執行
javac –verbose A.java
如果操作無誤,那麼您的螢幕上會顯示底下訊息:
注意,如果您在 d:\my 底下仍保有原本的B.java,則會產生底下錯誤訊息。
從這個輸出您可以發現,編譯器總是先到 A.java 本身所在的路徑中尋找
B.java,雖然編譯器找到了B.java,可是比對過其package 宣告之後,認為
它應該位於edu\nctu 目錄下,不該在此目錄下,因此產生錯誤訊息。這個動
作在本章後面會有更詳細的解釋。
不管如何,這次終於編譯成功了!! 您會在d:\my 目錄下找到A.class,也會在
d:\my\edu\nctu 目錄下找到編譯過的B.class。編譯成功之後,請輸入指令:
java A
您會看到螢幕上輸出
package test
程式順利地執行了。接這著咱們做個測試,請將d:\my\edu\nctu 目錄下的
B.class 移除,重新執行
java A
螢幕上會輸出錯誤訊息:
意思是說 java.exe 無法在edu/nctu 這個目錄下找到B.class。完成了這
個實驗之後,請將剛剛移除的B.class 還原,以便往後的討論。
到此處,我們得到了兩個重要的結論:
結論一:
如果您的類別屬於某個package,那麼您就應該將它至於該package 所對應
的相對路徑之下。舉例來說,如果您有個類別叫做C,屬於xyz.pqr.abc 套件,
那麼您就必須建立一個三層的目錄 xyz\pqr\abr,然後將C.java 或是C.class
放置到這個目錄下,才能讓javac.exe 或是java.exe 順利執行。
其實這裡少說了一個重點,就是這個新建的目錄應該從哪裡開始? 一定要從
d:\my 底下開始建立嗎? 請大家將這個問題保留在心裡,我們將在底下的討論
之中為大家說明。
結論二:
當您使用javac.exe 編譯的時候,類別原始碼的位置一定要根據結論一所說來
放置,如果該原始碼出現在不該出現的地方(如上述測試中B.java 同時存在
d:\my 與d:\my\edu\nctu 之下),除了很容易造成混淆不清,而且有時候抓
不出編譯為何發生錯誤,因為javac.exe 輸出的錯誤訊息根本無法改善問題。
在我們做進一步測試討論前,請再做一個測試,請將d:\my\edu\nctu 目
錄下的B.class 複製到d:\my 目錄中,執行指令:
java A
您應當還是會看到螢幕上輸出
package test
但是此時如果您重新使用編譯指令
javac A.java
重新編譯A 類別,螢幕上會出現
bad class file: .\B.class
class file contains wrong class: edu.nctu.B
的錯誤訊息。
最 後 , 請 您 刪 掉 d:\my 底下剛剛複製過來的B.class , 也刪除
d:\my\edu\nctu 目錄中的B.java , 也就是讓整個系統中只剩下
d:\my\edu\nctu 目錄中擁有B.class,然後再使用指令
javac A.java
您一定會發現,除了可以通過編譯之外,也可以順利地執行。
在測試中,我們又得到了兩個結論:
結論三:
編譯時,如果程式裡用到其他的類別,不需要該類別的原始碼也一樣能夠通過編
譯。
結論四:
當您使用javac.exe 編譯程式卻又沒有該類別的原始碼時,類別檔放置的位置
應該根據結論一所說的方式放置。如果類別檔出現在不該出現的地方(如上述測
試中B.class 同時存在d:\my 與d:\my\edu\nctu 之下),有很大的可能性會
造成難以的編譯錯誤。雖然上述的測試中,使用java.exe 執行Java 程式時,
類別檔亂放不會造成執行錯誤,但是還是建議您儘量不要這樣做,除了沒有意義
之外,這種做法像是一顆不定時炸彈,隨時都有可能造成您的困擾。
討論三:
請先刪除討論二中所產生的所有類別檔,接著請將d:\my 裡的edu 目錄連
同子目錄全部移到(不是複製喔!)d:\底下。然後執行
javac A.java
糟了,螢幕上又出現三個錯誤,意思是說找不到edu.nctu 這個package。其
他兩個錯誤都是因為第一個錯誤所衍生。
接著請執行修改過的指令
javac –classpath d:\ A.java
就可以編譯成功,您可以在d:\my 目錄下找到A.class,也可以在d:\edu\nctu
目錄下找到B.class。
請執行指令
java A
又出現錯誤訊息了,意思是說java.exe 無法在edu\nctu 這個目錄下找到
B.class。
聰明的你一定想到解決方法了,請執行修改過的指令
java –classpath d:\ A
可式螢幕上仍然出現錯誤訊息,但是這回java.exe 告訴您他找不到A.class,
而不是找不到B.class。
好吧,那再將指令改成
java –classpath d:\;. A
哈哈!這回終於成功地執行了。
到此,我們歸納出一個新的結論,但是在此之前請回過頭看一次先前的結論
一,再回來看新結論:
結論五:
結論一中我們提到,如果您有個類別叫做C,屬於xyz.pqr.abc 套件,那麼您
就必須建立一個三層的目錄 xyz\pqr\abr,然後將C.java 或是C.class 放置到
這個目錄下,才能讓javac.exe 或是java.exe 順利執行。但是這個新建的目錄
應該從哪裡開始呢? 答案是:”可以建立在任何地方”。但是您必須告訴java.exe
與javac.exe 到哪裡去找才行,告訴的方式就是利用它們的-classpath 選項。
在此測試中,我們把edu\nctu 這個目錄移到d:\之下,所以我們必須使用指令:
javac –classpath d:\ A.java

java –classpath d:\;. A
告訴java.exe 與javac.exe 說:”如果你要找尋類別檔,請到-classpath 選項後
面指定的路徑去找,如果找不到就算了”。
不過這裡又衍生出兩個問題,首先第一個問題是,那麼為什麼之前的指令都不需
要額外指定-classpath 選項呢?這是因為當您執行java.exe 和javac.exe 時,
其實他已經自動幫您加了參數,所以不管是討論一或討論二裡所使用的指令,其
實執行時的樣子如下:
javac –classpath . A.java

java –classpath . A
意思就是說,他們都把當時您所在的目錄當作是-classpath 選項的預設參數。
那麼您可以發現,如果是javac.exe 執行時,當時我們所在的路徑是d:\my,
所以很自然地,他會自動到d:\my 底下的edu\nctu 目錄尋找需要的檔案,
java.exe 也是一樣的道理。
但是,一旦我們搬移了 edu\nctu 目錄之後,這個預設的參數使得
javac.exe 要尋找d:\my\edu\nctu 底下的B.java 或B.class 來進行編譯時,
發生連d:\my\edu\nctu 目錄都找不到的情況(因為被我們移走了),所以自然
發生編譯錯誤。因此我們必須使用在指令中加上–classpath d:\,讓javac.exe
到d:\下尋找相對路徑d:\edu\nctu。執行java.exe 時之所以也要在指令中加
上–classpath d:\,也是一樣的道理。
如果您仔細比較兩個修改過的指令,您還是會發現些許的差異,於是引發了
第二個問題:為什麼javac.exe 用的是–classpath d:\,而java.exe 如果不
用–classpath d:\;.而只用–classpath d:\卻無法執行呢? 我們在第二章曾經提過,
-classpath 是用來指引AppClassLoader 到何處載入我們所需要的類別。在執
行時期,主程式A.class 也是一個類別,所以如果不是預設的情況(預設指向目
前所在目錄,即”.”),我們一定要向AppClassLoader 交代清楚所有相關類別
檔的所在位置。但是對編譯器來說,A.java 就單純是個檔案,只要在目前目錄
之下,編譯器就能找到,基本上和AppClassLoader 毫無關聯,需要在
-classpath 指定路徑,只是為了在編譯時期AppClassLoader 可以幫我們載入
B.class 或指引編譯器找到B.java,所以必須這樣指定。因此,對javac.exe
來說,-classpath 有上述兩種作用;然而對於java.exe 來說,就只有傳遞給
AppClassLoader 一種作用而已。
那麼您一定會問,如果每次編譯會執行都要打那麼長的指令,那豈不是非常
麻煩,雖然java.exe 也提供一個簡化的選項–cp(功能同–classpath),但是還是
很麻煩。為了解決這個問題,所以設計了一個名為CLASSPATH 的環境變數,這
個環境變數可以省掉您不少麻煩。
您可以在命令列打上
set CLASSPATH=d:\;.
然後您就可以像之前一樣使用指令:
javac A.java

java A
完成編譯和執行的工作。但是請注意,如果您設定了環境變數CLASSPATH,而
在使用java.exe 或javac.exe 的時候也使用了–classpath 選項,那麼環境變數
將視為無效,而改為以了–classpath 選項所指定的路徑為準。請注意,環境變數
CLASSPATH 與–classpath 選項分別所指定的路徑並不會有加成效果。
JAR 檔與ZIP 檔的效用
如果上述討論都沒有問題了,接下來請利用Winzip 之類的壓縮工具將
B.java 封裝到edu.zip 之中,並將edu.zip 放到d:\之下。edu.zip 內容如下圖
所示:
請執行
javac –classpath d:\edu.zip A.java
會出現錯誤訊息如下:
從這裡您可以知道,原始碼檔放在壓縮檔之中是沒有任何效果的。前面既然說過
編譯或執行都可以不需要原始碼,所以改將B.class 封在edu.zip 之中,edu.zip
的內容變成如下所示:
請重新執行
javac –classpath d:\edu.zip A.java
您會發現可以通過編譯。同時您也可以使用
java –classpath d:\edu.zip;. A
一樣可以成功地執行。
到這裡我們又歸納出一個新結論:
結論六:
使用ZIP 檔的效果和單純的目錄相同,如果您在–classpath 選項指定了目錄,就
是告訴java.exe 和javac.exe 到該目錄尋找類別檔;如果您在–classpath 選項
指定了ZIP 檔的檔名,那麼就是請java.exe 和javac.exe 到該壓縮檔中尋找類
別檔。請注意,即使是在壓縮檔中,但是該有的相對路徑還是要有,否則java.exe
和javac.exe 仍然無法找到他們所需要的類別檔。
當然,在環境變數 CLASSPATH 中也可以指定ZIP 檔作為其內容。
為何這此我們要提出 ZIP 這種格式的壓縮檔呢? 如果您常常使用別人所開
發的套件,您一定會發現別人很喜歡使用JAR 檔(.jar)這種檔案格式。其實JAR
檔就是ZIP 檔。為何大家喜歡使用JAR 檔的封裝方式呢?這是因為一般別人所開
發的套件總是會附帶許多類別檔和資源檔(例如圖形檔),這麼多檔案全部放在目
錄下很容易讓人感到雜亂不堪,如果將這些檔案全部封裝在一個壓所檔之中,不
但可以減少檔案大小,也可以讓您的硬碟看起來更精簡。這也是為何每次我們下
載了某人所開發的套件時,如果只有一個JAR 檔,我們就必須在環境變數
CLASSPATH 或–classpath 選項中加上這個JAR 檔的檔名,因為唯有如此,我們
才能讓java.exe 和javac.exe找到我們的程式中所使用到別人套件中類別的類
別檔,並成功執行。(在第二章中,我們也曾經提到可以放到<JRE 所在目錄
\lib\ext 底下,請回頭參閱第二章)
路徑的順序問題
在這個討論中最後要提的是,CLASSPATH 或–classpath 選項中所指定的路
徑或JAR 檔(或ZIP 檔)的先後次序也是有影響的。
舉例來說,我們分別把 B.java 放在d:\edu\nctu 與d:\my\edu\nctu
目錄下,然後鍵入指令:
javac –classpath d:\ A.java
您將發現編譯器編譯的是d:\edu\nctu 目錄下的B.java。我們如果將指令修改
為:
javac –classpath .;d:\ A.java
您將發現編譯器編譯的是d:\my\edu\nctu 目錄下的B.java。所以在指定環
境變數CLASSPATH 或–classpath 選項時,所給予的目錄名稱或JAR 檔名稱之順
序一定要謹慎。
之所以要特別提醒大家這個問題,是因為常常在寫程式的時候不小心在兩個
地方都有相同的套件(這也是筆者本身的切身之痛),一個比較舊,但指定
classpath 的時候該路徑較前,導致雖然另外一個路徑裡有新版的程式,但是執
行或編譯時卻總是跑出舊版程式的執行結果,讓人丈二金剛摸不著腦袋。所以最
後還是要提醒您特別注意正視這個問題。
討論四:
請刪除前面討論中製作的所有 JAVA 檔和類別檔,並重新在d:\my 目錄下
產生一個新檔案 A.java,內容如下:
A.java
package edu.nctu.mot ;
public class A
{
public static void main(String[] args)
{
System.out.println("I'm A") ;
}
}
在此我們把類別A 歸屬到edu.nctu.mot 套件之下。
接著請在命令列輸入指令:
javac A.java
咦? 您會發現竟然可以成功地編譯,這是怎麼一回事呢? 其實這裡的A.java 和
前面的B.java是有些許不同的。雖然A.java屬於edu.nctu.mot 套件,而B.java
屬於edu.nctu 套件。但是之前我們編譯B.java 時候並非直接編譯它,而是透
過javac.exe 類似make 的機制間接編譯B.java(因為類別A 中用到了類別
B)。可是在這裡,我們直接編譯A.java,沒有透過make 機制,所以可以編譯
成功(意思就是說前面討論中用到的B.java,即使不放在該放置的目錄中一樣可
以成功編譯,只要它自己以及所用到的類別可以在環境變數CLASSPATH
或–classpath 選項指定的位置找到即可)。
好,編譯成功了,現在我們試著執行看看,請輸入:
java A
相信您的螢幕上一定出現了許多錯誤。
接下來我們應當根據結論一所說,建立一個名為\edu\nctu\mot 的目錄,
並將A.java 至於其中,並刪除d:\my 裡面的A.java 和A.class。然後輸入指

javac A.java
螢幕上出現錯誤訊息
奇怪了,前面不是說 javac.exe 會內定從目前所在目錄尋找類別的原始碼呢?
這裡怎麼無法運作呢? 其實問題和前面一樣,這是因為現在我們直接要求
javac.exe 編譯A.java,而非透過make 機制間接編譯,所以我們的指令無法
讓javac.exe 清楚地知道我們的意圖,所以我們必須更改指令內容,您會想到
的指令可能有以下幾種:
1 javac edu.nctu.mot.A.java
2 javac -classpath .\my\edu\nctu\mot A.java
3 javac -classpath d:\my\edu\nctu\mot A.java
4 javac d:\my\edu\nctu\mot\A.java
其中,第一種指令是對Java 比較熟悉的人可能會想到的方法,意義我們會
在稍後介紹。第二和第三種指令其實意思是一樣的,只是一個用了相對路徑,一
個用了絕對路徑。但是,真正能夠成功編譯A.java 的卻只有第四種指令。所以
從這裡可以得知,如果您的專案中充滿了許多類別,請儘量利用javac.exe 的
make 機制來自動幫您編譯程式碼,否則可是很辛苦的。
現在我們試著執行,請輸入:
java A
java.exe 說他找不到類別A。
我們必須更改指令內容,您會想到的指令可能有以下幾種:
1 java edu.nctu.mot.A
2 java -classpath .\edu\nctu\mot A
3 java -classpath d:\my\edu\nctu\mot A
4 java d:\my\edu\nctu\mot\A
第二和第三種指令其實意思是一樣的,只是一個用了相對路徑,一個用了絕對路
徑,但是,真正能夠執行的卻只有第一種指令。這個指令的意思是告訴java.exe
說:”請你根據環境變數CLASSPATH 或–classpath 選項指定的位置執行相對路徑
\edu\nctu\mot 之下的A.class”,前面我們提過,如果您沒有指定環境變數
CLASSPATH 或–classpath 選項,預設會用目前所在路徑,所以第一種指令真正
執行時應該是:
java –classpath . edu.nctu.mot.A
因 為 當 時 我 們 所 在 的 路 徑 為 d:\my , 所以很自然地java.exe 會尋找
d:\my\edu\nctu\mot 目錄中的A.class 來執行。
根據結論五所說,如果你將指令改成
java –classpath d:\ edu.nctu.mot.A
那麼就無法執行了,除非您在d:\edu\nctu\mot 裡也有A.class。
以上做了那麼多測試,歸納了這幾點結論,最後請大家在利用package 與
import 機制的時候務必做到下列幾項工作:
1. 將 JAVA 檔和類別檔確實安置在其所歸屬之package 所對應的相對路
徑下。
2. 不管使用 java.exe 或javac.exe,最好明確指定–classpath 選項,如果
怕麻煩,使用環境變數CLASSPATH 可以省去您輸入額外指令的時間。
請注意在他們所指定的路徑或JAR 檔中是否存有package 名稱和類別
名稱相同的類別,因為名稱上的衝突很容易引起混淆。
只要確實做到上述事項,就可以減少螢幕上大量與實際問題不相干的錯誤訊息,
並確保您在Java 程式設計的旅途中順利航行。
在此要提醒大家,到此為止我們所舉的例子都是使用java.exe 和jvac.exe
的測試結果,但是在實際生活中,只要和Java 程式語言相關的工具,都會和
package 與import 機制息息相關。比方說如果您開發MIDlet,必定會用到
preverify.exe;如果要使用JNI,您就會用到javah.exe,使用這些工具時如果
您沒有遵循package 與import 機制的運作方式,可是無法得到您要的結果。
█深入package 與import 機制
在本章前半個部分,筆者向大家說明了初學者進入Java 程式設計領域常見
的重大門檻 - package 與import 機制。相信大家已經可以自行處理Class Not
Found 這一類Exception 了。但是,前半個部分所使用的討論方式只是工程上
的結果,那麼原理呢? 只要理解了這些錯誤之所以出現的理由,就可以知道編譯
之所以成功或錯誤的原因。因此,本章下半部要用理論的方式為大家說明
package 與import 機制運作的秘密。
接下來的討論都是假設你遵守「每一個類別都會根據其所屬的package 放
在正確的相對路徑上」。從前半個部分的討論我們可以知道,如果我們不把類別
放在正確的相對位置,則會產生許多與問題本身不相干的錯誤訊息,這這部分涉
及到Java 編譯器自己本身對錯誤的處理方式,所以我們不加以討論。
█編譯時期(Compile-time)的Package 運作機制
在講解編譯時期的 Package 運作機制之前,首先我們要先製作一個完整的
範例,我們測試所使用的目錄結構如下圖所示:
目錄: src
目錄: comtime 目錄: outer
目錄: com
目錄: edu
檔案: CA.java
檔案: Main.java
檔案: CA.java
檔案: CA.java
檔案: CA.java
目錄: com
檔案: CA.java
在我們的測試範例之中,我們總共有六個檔案,他們的內容分別如下:
src\comtime\Main.java
public class Main
{
public static void main(String args[])
{
CA ca = new CA() ;
}
}
src\comtime\CA.java
public class CA
{
}
src\comtime\edu\CA.java
package edu;
public class CA
{
}
src\comtime\com\CA.java
package com;
public class CA
{
}
src\outer\CA.java
package outer;
public class CA
{
}
src\com\CA.java
package com;
public class CA
{
}
接下來,我們將使用 src\comtime 作為我們測試的根目錄(意即:直接放置在此
目錄之下的類別可以不需屬於任何package,而屬於任何package 的類別將
可以以此目錄做相對參考點,根據自己所屬的package 產生相對應的目錄來放
置自己),因此請將提示符號切換到src\comtime 目錄,並執行
javac Main.java

分享到:
评论

相关推荐

    Java SPI机制原理及代码实例

    下面将详细介绍Java SPI的工作原理、使用方法以及示例。 1. SPI机制原理: - 服务接口:首先,定义一个公共的服务接口,例如`Search`,供其他组件使用。 - 服务实现:不同的提供商根据接口提供自己的实现,如`...

    rustful 的java实例代码

    通过这样的实践,你可以进一步了解RESTful服务的工作原理以及如何使用Java来实现它们。在实际开发中,你还可以利用更多的JAX-RS特性,如参数绑定、异常处理、JSON序列化等,以实现更复杂的业务逻辑。

    超轻量压缩传输js2java rpc框架(XtZPStream v1.0)

    &lt;load-on-startup&gt;3&lt;/load-on-startup&gt; &lt;servlet-name&gt;CommonHttpServlet &lt;url-pattern&gt;/CMHS ``` #### 如何使用 以Java端为例,下面是一个简单的方法调用示例: ```java package ...

    使用类分解器Javap分析Java字节码

    此外,`JVMIS.pdf`文件可能包含了更多关于Java虚拟机(JVM)内部结构和字节码解释的信息,这对于深入理解`Javap`的工作原理和Java程序的运行时行为至关重要。建议读者阅读此文档以获得更全面的知识。 总之,`Javap`...

    opencv封装动态链接库给Java使用JNI

    这些函数应该遵循Java的命名约定,因为JNI要求函数名遵循特定的命名规则(例如,`JNIEXPORT void JNICALL Java_package_class_name_functionName(JNIEnv* env, jobject obj, ...)`)。 3. 编译动态链接库:使用C++...

    Java实现上传和下载有关教程

    package load.up; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.util.List; import java.util.Date; import java.util.Iterator; import org.apache.commons.fileupload....

    java自定义类加载classloader文档,包括代码

    本文将深入探讨Java中的类加载机制,并通过一个具体的自定义类加载器的例子来帮助理解其工作原理。 #### 二、Java类加载器的基本概念 Java中的类加载器主要负责完成以下三个基本任务: 1. **加载(Loading)**:...

    java实现图片或文件的上传功能具体思路及代码.docx

    package load; import java.io.File; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax...

    shine05-jersey-rest-server-master_java_

    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt; &lt;servlet-name&gt;Jersey Web Application &lt;url-pattern&gt;/rest/* ``` 四、JSON序列化与反序列化 RESTful服务通常与JSON格式的数据交互。Jersey内置了支持JSON的能力,通过...

    javaweb编程中常见的异常及处理方法.doc

    在Java Web编程中,异常处理是一项至关重要的任务,因为它能够帮助开发者识别并修复程序中的...在进行Java Web开发时,理解和掌握异常处理以及框架的内部工作原理是非常必要的,它有助于编写出更健壮、更稳定的代码。

    S08-SnakeYaml反序列化1

    package test; public class MyClass { String value; public MyClass(String args) { value = args; } public String getValue() { return value; } } ``` 然后,你可以创建一个`MyClass`实例并使用...

    hibernate原理与应用

    《Hibernate原理与应用》 Hibernate 是一款流行的Java ORM(对象关系映射)框架,它解决了在面向对象编程中模型与关系数据库之间的“阻抗不匹配”问题。在Java应用程序中,我们通常使用对象来表示业务逻辑,而...

    java_学习资料

    - **Package配置**:定义一系列拦截器、结果类型等配置。 - **namespace配置**:为Action指定命名空间。 - **常量配置**:设置全局常量,如`struts.devMode`。 - **Result配置**:定义Action执行后的结果类型。 - **...

    java 小型环境搭建 SpringMVC

    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt; &lt;servlet-name&gt;dispatcher &lt;url-pattern&gt;/ &lt;!-- View Resolver --&gt; *.jsp &lt;page-encoding&gt;UTF-8 ``` 接着,在`src/main/webapp/WEB-INF`目录下创建`...

    自定义MVC框架

    - `&lt;%@ page language="java" import="java.util.*" pageEncoding="gbk"%&gt;` 这行代码指定了页面的脚本语言为Java,并导入了`java.util.*`包,同时设置了页面编码为GBK。 - `(); ... %&gt;` 用于获取当前项目的路径。 - ...

    DWR的介绍以及使用范例.还有使用DWR的相关配置

    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt; &lt;servlet-name&gt;dwr-invoker &lt;url-pattern&gt;/dwr/* ``` 3. **编写Java服务端程序**:创建一个名为HelloWorld的Java类,提供一个sayHello方法用于返回问候语。 ```java ...

    ClassLoader实例

    本文将深入探讨Java类加载器的层次结构、工作原理以及如何实现动态加载类。 首先,Java类加载器分为系统类加载器和自定义类加载器。系统类加载器包括引导类加载器、扩展类加载器和系统类加载器: 1. 引导类加载器...

Global site tag (gtag.js) - Google Analytics