Java package的概念及類加載與運行

在瞭解任何其它的語法之前,我們首先要理解,如何組織你的JAVA源文件。我們以後將編寫越來越多的JAVA源文件,難道把它們全部放到一個目錄下面嗎?當然不是這樣的。在源代碼變得混亂不可維護之前,我們首先要了解代碼的組織方法。我們可以利用JAVA中“包(package)”的概念來組織你的代碼。Package這種概念實際上跟JAVA中的安全模型有關,但安全模型不是我們現在要了解的,這些東西以後在適當的時候,我會跟大家講清楚的。現在,我們理解“包”這種概念,它就是一個目錄樹結構。

 

觀察操作系統裏面的目錄結構,你就能理解“包”這個概念的作用。

 

創建一個類(Test01.java), 先不聲明包,編譯和運行,然後聲明一個包:mypkgs.examples,並放到一個目錄樹下面:

然後編譯和運行,觀察它的錯誤!

java.lang.NoClassDefFoundError: Test01 (wrong name: mypkgs/examples/Test01)

 

爲什麼呢?當我們在一個類中聲明瞭一個包,那麼在運行的時候,必須指定這個類的全路徑類名!java mypkgs.examples.Test01這樣的全路徑類名來運行它!觀察錯誤:

java.lang.NoClassDefFoundError: mypkgs/examples/Test01

 

這種異常,大家以後肯定會經常碰到,它的出現跟很多因素有關。以後,我們會逐步瞭解所有的機制!

 

如果我們要運行一個聲明瞭包的類,必須爲這個包創建一個目錄結構,並把這個類(.class文件)放到那個目錄下面,併到包的根目錄下用全路徑類名來運行它!

 

如果自行創建一個目錄結構太麻煩的話,那麼我們可以在編譯的時候,指定編譯器自動幫我們根據包結構創建一個目錄結構如何?【用javac –d 命令編譯並運行】

-d選項是指定類的輸出目錄的根

 

如果不指定-d選項,那麼編譯器會把.class文件放在跟.java文件一個目錄下!

 

擴展思考:

 

l           包的命名

那麼,我們通常是怎麼創建這樣的一個目錄結構的呢?雖然你可以隨意創建一些目錄來存放你的代碼,但爲了不讓其它人把你當成“菜鳥”,我們需要遵守一定的編程規範,這些編程規範是由SUN公司制定的,並被業界衆多JAVA程序員遵守的寫程序通用原則。這些原則不是強制性的規則,而只是參考意見,你可以不遵守這些原則,它不會導致你的程序不能運行,只不過如果你遵守這些原則的話,會顯得更加專業,而且容易閱讀和理解。這些通用原則包括:如何給一個類起名字;如何給一個變量起名字;如何創建包結構;如何寫代碼註釋……等等,大家以後寫程序的時候,應該強迫自己遵守這些約定,並讓它成爲你的一種習慣。就是說,到你不需要在心裏告訴自己如何去定義這些類//變量等,而是非常自然的就按照習慣去定義的時候,你纔算“出師”了! J

 

請參考:http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html 閱讀有關的編碼原則。

 

包的命名通常是小寫字母,一般以com/org等開頭,然後是公司/組織名稱,然後是項目名稱等。

 

SayHello.java放到一個包中。

 

我們把SayHello放到了某個目錄下面,我們以source目錄爲根,以後的代碼都放在這個根目錄下面,現在,我們編譯SayHello.java

 

這個source目錄,我們稱之爲“源代碼目錄”,在這個目錄下面,將按照包的規則,創建子目錄,然後把JAVA源文件放到某個目錄下面。這樣,我們必須更改SayHello.java源文件,在它的第一行,加上包的聲明:package cn.com.leadfar.learning;

 

演示編譯運行

 

l           在哪裏執行javac.exe以編譯代碼?

n         可以在任何目錄下執行javac.exe

n         只要在編譯的時候,指定了正確的.java文件路徑即可,比如在source下面編譯可以用:javac cn/com/leadfar/learning/ SayHello.java 命令來編譯;或者可以直接進入learning目錄,運行:javac SayHello.java來編譯

n         編譯之後,.class文件被放置到和.java文件一樣的目錄中

n         因爲沒有指定.class文件輸出到別的目錄,所以,現在.java源代碼文件和.class文件都在一起,這種情況下,“源代碼目錄”同時也是“類路徑目錄

n         所謂“類路徑目錄”,即類存放的根目錄(在根目錄下,按照類的包結構創建子目錄來存放!

u       進入source的父目錄,創建classes目錄

u       運行:

u       JavaSE-02>javac -d classes source/cn/com/leadfar/learning/SayHello.java

u       表示將.class文件輸出到classes目錄(請觀察classes目錄中的生成內容!)

n         這個classes,即類路徑目錄,類路徑(CLASSPATH)的概念稍後解釋

l           如何以及在哪裏執行java.exe以運行SayHello類?

n         如果一個類名被放置在一個包裏面,那麼我們運行的時候必需指定類的全路徑類名

n         java cn.com.leadfar.learning.SayHello

n         那麼這個命令應該在哪裏運行呢,我們應該在source目錄中運行

n         在別的目錄中是否可以運行?

u       嘗試一下,轉到上一級目錄運行java命令(演示)

u       Exception in thread "main" java.lang.NoClassDefFoundError: cn/com/leadfar/learning/SayHello

u       Caused by: java.lang.ClassNotFoundException: cn.com.leadfar.learning.SayHello

u               at java.net.URLClassLoader$1.run(URLClassLoader.java:202)

u               at java.security.AccessController.doPrivileged(Native Method)

u               at java.net.URLClassLoader.findClass(URLClassLoader.java:190)

u               at java.lang.ClassLoader.loadClass(ClassLoader.java:307)

u               at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)

u               at java.lang.ClassLoader.loadClass(ClassLoader.java:248)

u       Could not find the main class: cn.com.leadfar.learning.SayHello.  Program will exit.

u       爲什麼會出現類找不到的異常呢?那是因爲java虛擬機在當前運行目錄中找不到cn/com/leadfar/learning這樣的目錄結構,自然也找不到類了。

u       如果不指定CLASSPATH環境變量,java虛擬機會在“工作目錄”下尋找相應的類,所謂工作目錄,缺省情況下,就是在哪個目錄運行java命令,哪個目錄就是“工作目錄”。

u       現在,“工作目錄”就是source目錄的父目錄

u       所以,現在我們需要指定CLASSPATH環境變量

l           在別的目錄中執行其它目錄中的類?CLASSPATH的概念

n         CLASSPATH的概念

u       虛擬機使用類加載器將類加載到內存中運行

u       虛擬機根據CLASSPATH環境變量所指定的路徑,來按順序搜索及加載類

u       CLASSPATH是一個目錄或.jar文件的列表,用“;”分號分隔(操作系統不同,分隔符也不同,比如在Linux下面用“:”冒號來作爲分隔符)

n         如何指定CLASSPATH

u       java –classpath source cn.com.leadfar.learning.SayHello,將正確運行

l           更進一步的思考:可不可以不指定CLASSPATH,而運行SayHello - 工作目錄的概念

u       回答:指定“工作目錄”

l         缺省的工作目錄是當前運行java程序所在的目錄

l         可以用-Duser.dir=xxx來指定工作目錄

l         java -Duser.dir=source cn.com.leadfar.learning.SayHello,即可

u       理解工作目錄的概念,有利於理解CLASSPATH

u       經常有人習慣將CLASSPATH定義爲包含“.”,即一個英文句點,表示當前目錄的意思,這個所謂的“當前目錄”,就是“工作目錄”,而不一定是運行java.exe所在的目錄

u       進入source目錄,運行:

u       java cn.com.leadfar.learning.SayHelloOK

u       java –Duser.dir=. cn.com.leadfar.learning.SayHelloOK

u       java –Duser.dir=com cn.com.leadfar.learning.SayHelloERROR

l         出現類沒找到的錯誤(原因是沒有指定CLASSPATH,缺省以工作目錄進行搜索)

u       java –Duser.dir=com –classpath . cn.com.leadfar.learning.SayHelloERROR

l         出現類沒找到的錯誤(原因是雖然指定了CLASSPATH,但它是一個相對路徑,這相對的概念是相對於工作目錄而言,而不是當前運行java.exe這個目錄)

u       java –Duser.dir=com –classpath .. cn.com.leadfar.learning.SayHelloOK

u       java –Duser.dir=com –classpath D:/xx/xx/source cn.com.leadfar.learning.SayHelloOK

l         這回指定了絕對路徑作爲CLASSPATH,它就與工作目錄無關了!

u       java –Duser.dir=com/a/b/c/d –classpath D:/xx/xx/source cn.com.leadfar.learning.SayHelloOK

l         工作目錄,實際上可以是任何值(一個實際不存在的目錄也可以,但最好不要這樣幹)

n         要注意,類路徑的定義方法是-classpath加空格,然後加路徑列表,而不是-classpath=路徑列表,路徑列表,用“;”分號分隔!

n         我們SayHello類中引入的另外一個類System在哪裏?

u       JAVA中一個一個類,就像一個一個動態鏈接庫一樣,在需要的時候,JVM纔會去加載

u       System這個類,在這裏:C:/Program Files/Java/jdk1.6.0_18/jre/lib/rt.jar

l         一個jar擴展名的文件,它是一系列.class文件(也可以包含其它想包含的文件)壓縮之後形成的一個包

l         一個jar擴展名的文件,可以作爲CLASSPATH的一部分

l         rt.jar這個包,包含了JavaSE標準API的所有類,我們在JAVA API文檔中能夠查找到的類,都在這個包裏。

u       爲什麼沒有把rt.jar這個包加到我們的CLASSPATH中?

l         因爲它由JVM自動加載,而且它的位置是固定的,無需指定

l           更加深層次的再次思考:可不可以既不指定CLASSPATH,也不指定工作目錄,而仍然能夠運行SayHello

n         Java虛擬機運行一個類的時候,是否能夠加載一個類,取決於類加載器是否能夠找到這個類!類加載器的概念,後面會深入探討,現在只從字面上去理解它。類加載器就是加載一個類用的!缺省情況下,JAVA虛擬機會依次使用以下三個類加載器來搜索和加載一個類:啓動類加載器、擴展類加載器、類路徑類加載器!

u       啓動類加載器就是搜索rt.jar包(實際上就是一個壓縮文件,裏面包含很多類文件),嘗試從其中加載一個類,如果沒有找到,則

u       通過擴展類加載器,搜索ext目錄(擴展目錄,注意這個目錄在JDK下面的JRE下)中所有的jar包,搜索這些jar包中是否有要加載的類,如果沒有找到,則

u       通過類路徑類加載器,搜索類路徑中所有的類,如果還是沒有找到,則拋出異常!

u       所以答案就是:利用JAVA中的“擴展目錄”。

u       JAVA中的擴展目錄是:C:/Program Files/Java/jdk1.6.0_18/jre/lib/ext

u       在這個擴展目錄jar包中包含的類,也無需使用CLASSPATH指定,即可自動加載

u       所以,我們只要把SayHello類打成jar包,並把它拷貝到這個目錄下即可

l         我們進入source目錄,執行以下命令:

l         jar cvf sayhello.jar .

l         表示把當前目錄下的所有內容打包成sayhello.jar文件

l         jar.exe這個命令位於JDK開發工具包下的bin目錄

l         或我們進入source目錄的父目錄,執行以下命令:

l         jar cvf sayhello.jar –C source . 也可以打包成sayhello.jar

l          

u       打包,拷貝到擴展目錄中,我們進入任何一個目錄,都可以運行:

l         java cn.com.leadfar.learning.SayHello

 

總結,我們從“包”的概念出發,描述了:

l           如何將源代碼按照一定的包結構來進行組織

l           如果源代碼被放到一個包中,則必須在代碼中增加package聲明

l           理解源代碼目錄(即存放源代碼的目錄)

l           如果源代碼被放到一個包中,編譯時(javac),需指定路徑

l           如果源代碼被放到一個包中,編譯後,.class文件將與源文件在同一個目錄結構中存放

l           對於被放到某個包中的類,運行的時候,需要使用全路徑類名,理解全路徑類名的概念

l           理解“工作目錄”、“類路徑”、“擴展目錄”的概念,及其聯繫

l           使用jar命令,打包.class文件

發佈了34 篇原創文章 · 獲贊 30 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章