常常在網(wǎng)上看到有人詢問:如何把?java?程序編譯成?.exe?文件。通常回答只有兩種,一種是制作一個(gè)可執(zhí)行的?JAR?文件包,然后就可以像.chm?文檔一樣雙擊運(yùn)行了;而另一種是使用?JET?來(lái)進(jìn)行?編譯。但是?JET?是要用錢買的,而且據(jù)說?JET?也不是能把所有的?Java?程序都編譯成執(zhí)行文件,性能也要打些折扣。所以,使用制作可執(zhí)行?JAR?文件包的方法就是最佳選擇了,何況它還能保持?Java?的跨平臺(tái)特性。?
下面就來(lái)看看什么是?JAR?文件包吧:?
1.?JAR?文件包?
JAR?文件就是?Java?Archive?File,顧名思意,它的應(yīng)用是與?Java?息息相關(guān)的,是?Java?的一種文檔格式。JAR?文件非常類似?ZIP?文件??準(zhǔn)確的說,它就是?ZIP?文件,所以叫它文件包。JAR?文件與?ZIP?文件唯一的區(qū)別就是在?JAR?文件的內(nèi)容中,包含了一個(gè)?META-INF/MANIFEST.MF?文件,這個(gè)文件是在生成?JAR?文件的時(shí)候自動(dòng)創(chuàng)建的。舉個(gè)例子,如果我們具有如下目錄結(jié)構(gòu)的一些文件:?
==?
`--?test?
?`--?Test.class?
把它壓縮成?ZIP?文件?test.zip,則這個(gè)?ZIP?文件的內(nèi)部目錄結(jié)構(gòu)為:?
test.zip?
`--?test?
?`--?Test.class?
如果我們使用?JDK?的?jar?命令把它打成?JAR?文件包?test.jar,則這個(gè)?JAR?文件的內(nèi)部目錄結(jié)構(gòu)為:?
test.jar?
|--?META-INF?
| ?`--?MANIFEST.MF?
`--?test?
`--Test.class?
2.?創(chuàng)建可執(zhí)行的?JAR?文件包?
制作一個(gè)可執(zhí)行的?JAR?文件包來(lái)發(fā)布你的程序是?JAR?文件包最典型的用法。?
Java?程序是由若干個(gè)?.class?文件組成的。這些?.class?文件必須根據(jù)它們所屬的包不同而分級(jí)分目錄存放;運(yùn)行前需要把所有用到的包的根目錄指定給?CLASSPATH?環(huán)境變量或者?java?命令的?-cp?參數(shù);運(yùn)行時(shí)還要到控制臺(tái)下去使用?java?命令來(lái)運(yùn)行,如果需要直接雙擊運(yùn)行必須寫?Windows?的批處理文件?(.bat)?或者?Linux?的?Shell?程序。因此,許多人說,Java?是一種方便開發(fā)者苦了用戶的程序設(shè)計(jì)語(yǔ)言。?
其實(shí)不然,如果開發(fā)者能夠制作一個(gè)可執(zhí)行的?JAR?文件包交給用戶,那么用戶使用起來(lái)就方便了。在?Windows?下安裝?JRE?(Java?Runtime?Environment)?的時(shí)候,安裝文件會(huì)將?.jar?文件映射給?javaw.exe?打開。那么,對(duì)于一個(gè)可執(zhí)行的?JAR?文件包,用戶只需要雙擊它就可以運(yùn)行程序了,和閱讀?.chm?文檔一樣方便?(.chm?文檔默認(rèn)是由?hh.exe?打開的)。那么,現(xiàn)在的關(guān)鍵,就是如何來(lái)創(chuàng)建這個(gè)可執(zhí)行的?JAR?文件包。?
創(chuàng)建可執(zhí)行的?JAR?文件包,需要使用帶?cvfm?參數(shù)的?jar?命令,同樣以上述?test?目錄為例,命令如下:?
jar?cvfm?test.jar?manifest.mf?test?
這里?test.jar?和?manifest.mf?兩個(gè)文件,分別是對(duì)應(yīng)的參數(shù)?f?和?m,其重頭戲在?manifest.mf。因?yàn)橐獎(jiǎng)?chuàng)建可執(zhí)行的?JAR?文件包,光靠指定一個(gè)?manifest.mf?文件是不夠的,因?yàn)?MANIFEST?是?JAR?文件包的特征,可執(zhí)行的?JAR?文件包和不可執(zhí)行的?JAR?文件包都包含?MANIFEST。關(guān)鍵在于可執(zhí)行?JAR?文件包的?MANIFEST,其內(nèi)容包含了?Main-Class?一項(xiàng)。這在?MANIFEST?中書寫格式如下:?
Main-Class:?可執(zhí)行主類全名(包含包名)?
例如,假設(shè)上例中的?Test.class?是屬于?test?包的,而且是可執(zhí)行的類?(定義了?public?static?void?main(String[])?方法),那么這個(gè)?manifest.mf?可以編輯如下:?
Main-Class:?test.Test?<回車>?
這個(gè)?manifest.mf?可以放在任何位置,也可以是其它的文件名,只需要有?Main-Class:?test.Test?一行,且該行以一個(gè)回車符結(jié)束即可。創(chuàng)建了?manifest.mf?文件之后,我們的目錄結(jié)構(gòu)變?yōu)椋?
==?
|--?test?
| ?`--?Test.class?
`--?manifest.mf?
這時(shí)候,需要到?test?目錄的上級(jí)目錄中去使用?jar?命令來(lái)創(chuàng)建?JAR?文件包。也就是在目錄樹中使用“==”表示的那個(gè)目錄中,使用如下命令:?
jar?cvfm?test.jar?manifest.mf?test?
之后在“==”目錄中創(chuàng)建了?test.jar,這個(gè)?test.jar?就是執(zhí)行的?JAR?文件包。運(yùn)行時(shí)只需要使用?java?-jar?test.jar?命令即可。?
需要注意的是,創(chuàng)建的?JAR?文件包中需要包含完整的、與?Java?程序的包結(jié)構(gòu)對(duì)應(yīng)的目錄結(jié)構(gòu),就像上例一樣。而?Main-Class?指定的類,也必須是完整的、包含包路徑的類名,如上例的?test.Test;而且在沒有打成?JAR?文件包之前可以使用?java?<類名>?來(lái)運(yùn)行這個(gè)類,即在上例中?java?test.Test?是可以正確運(yùn)行的?(當(dāng)然要在?CLASSPATH?正確的情況下)。? 3.?jar?命令詳解?
jar?是隨?JDK?安裝的,在?JDK?安裝目錄下的?bin?目錄中,Windows?下文件名為?jar.exe,Linux?下文件名為?jar。它的運(yùn)行需要用到?JDK?安裝目錄下?lib?目錄中的?tools.jar?文件。不過我們除了安裝?JDK?什么也不需要做,因?yàn)?SUN?已經(jīng)幫我們做好了。我們甚至不需要將?tools.jar?放到?CLASSPATH?中。?
使用不帶任何的?jar?命令我們可以看到?jar?命令的用法如下:?
jar?{ctxu}[vfm0M]?[jar-文件]?[manifest-文件]?[-C?目錄]?文件名?...?
其中?{ctxu}?是?jar?命令的子命令,每次?jar?命令只能包含?ctxu?中的一個(gè),它們分別表示:?
-c 創(chuàng)建新的?JAR?文件包?
-t 列出?JAR?文件包的內(nèi)容列表?
-x 展開?JAR?文件包的指定文件或者所有文件?
-u 更新已存在的?JAR?文件包?(添加文件到?JAR?文件包中)?
[vfm0M]?中的選項(xiàng)可以任選,也可以不選,它們是?jar?命令的選項(xiàng)參數(shù)?
-v 生成詳細(xì)報(bào)告并打印到標(biāo)準(zhǔn)輸出?
-f 指定?JAR?文件名,通常這個(gè)參數(shù)是必須的?
-m 指定需要包含的?MANIFEST?清單文件?
-0 只存儲(chǔ),不壓縮,這樣產(chǎn)生的?JAR?文件包會(huì)比不用該參數(shù)產(chǎn)生的體積大,但速度更快?
-M 不產(chǎn)生所有項(xiàng)的清單(MANIFEST〕文件,此參數(shù)會(huì)忽略?-m?參數(shù)?
[jar-文件]?即需要生成、查看、更新或者解開的?JAR?文件包,它是?-f?參數(shù)的附屬參數(shù)?
[manifest-文件]?即?MANIFEST?清單文件,它是?-m?參數(shù)的附屬參數(shù)?
[-C?目錄]?表示轉(zhuǎn)到指定目錄下去執(zhí)行這個(gè)?jar?命令的操作。它相當(dāng)于先使用?cd?命令轉(zhuǎn)該目錄下再執(zhí)行不帶?-C?參數(shù)的?jar?命令,它只能在創(chuàng)建和更新?JAR?文件包的時(shí)候可用。 ?
文件名?...?指定一個(gè)文件/目錄列表,這些文件/目錄就是要添加到?JAR?文件包中的文件/目錄。如果指定了目錄,那么?jar?命令打包的時(shí)候會(huì)自動(dòng)把該目錄中的所有文件和子目錄打入包中。?
下面舉一些例子來(lái)說明?jar?命令的用法:?
1)?jar?cf?test.jar?test?
該命令沒有執(zhí)行過程的顯示,執(zhí)行結(jié)果是在當(dāng)前目錄生成了?test.jar?文件。如果當(dāng)前目錄已經(jīng)存在?test.jar,那么該文件將被覆蓋。?
2)?jar?cvf?test.jar?test?
該命令與上例中的結(jié)果相同,但是由于?v?參數(shù)的作用,顯示出了打包過程,如下:?
標(biāo)明清單(manifest)?
增加:test/(讀入=?0)?(寫出=?0)(存儲(chǔ)了?0%)?
增加:test/Test.class(讀入=?7)?(寫出=?6)(壓縮了?14%)?
3)?jar?cvfM?test.jar?test?
該命令與?2)?結(jié)果類似,但在生成的?test.jar?中沒有包含?META-INF/MANIFEST?文件,打包過程的信息也略有差別:?
增加:test/(讀入=?0)?(寫出=?0)(存儲(chǔ)了?0%)?
增加:test/Test.class(讀入=?7)?(寫出=?6)(壓縮了?14%)?
4)?jar?cvfm?test.jar?manifest.mf?test?
運(yùn)行結(jié)果與?2)?相似,顯示信息也相同,只是生成?JAR?包中的?META-INF/MANIFEST?內(nèi)容不同,是包含了?manifest.mf?的內(nèi)容?
5)?jar?tf?test.jar?
在?test.jar?已經(jīng)存在的情況下,可以查看?test.jar?中的內(nèi)容,如對(duì)于?2)?和?3)?生成的?test.jar?分別應(yīng)該此命令,結(jié)果如下;?
對(duì)于?2)?
META-INF/?
META-INF/MANIFEST.MF?
test/?
test/Test.class?
對(duì)于?3)?
test/?
test/Test.class?
6)?jar?tvf?test.jar?
除顯示?5)?中顯示的內(nèi)容外,還包括包內(nèi)文件的詳細(xì)信息,如:?
0?Wed?Jun?19?15:39:06?GMT?2002?META-INF/?
86?Wed?Jun?19?15:39:06?GMT?2002?META-INF/MANIFEST.MF?
0?Wed?Jun?19?15:33:04?GMT?2002?test/?
7?Wed?Jun?19?15:33:04?GMT?2002?test/Test.class?
7)?jar?xf?test.jar?
解開?test.jar?到當(dāng)前目錄,不顯示任何信息,對(duì)于?2)?生成的?test.jar,解開后的目錄結(jié)構(gòu)如下:?
==?
|--?META-INF?
| ?`--?MANIFEST?
`--?test?
`--Test.class?
jar?xvf?test.jar?
運(yùn)行結(jié)果與?7)?相同,對(duì)于解壓過程有詳細(xì)信息顯示,如:?
創(chuàng)建:META-INF/?
展開:META-INF/MANIFEST.MF?
創(chuàng)建:test/?
展開:test/Test.class?
9)?jar?uf?test.jar?manifest.mf?
在?test.jar?中添加了文件?manifest.mf,此使用?jar?tf?來(lái)查看?test.jar?可以發(fā)現(xiàn)?test.jar?中比原來(lái)多了一個(gè)?manifest。這里順便提一下,如果使用?-m?參數(shù)并指定?manifest.mf?文件,那么?manifest.mf?是作為清單文件?MANIFEST?來(lái)使用的,它的內(nèi)容會(huì)被添加到?MANIFEST?中;但是,如果作為一般文件添加到?JAR?文件包中,它跟一般文件無(wú)異。?
10)?jar?uvf?test.jar?manifest.mf?
與?9)?結(jié)果相同,同時(shí)有詳細(xì)信息顯示,如:?
增加:manifest.mf(讀入=?17)?(寫出=?19)(壓縮了?-11%)?
4.?關(guān)于?JAR?文件包的一些技巧?
1)?使用?unzip?來(lái)解壓?JAR?文件?
在介紹?JAR?文件的時(shí)候就已經(jīng)說過了,JAR?文件實(shí)際上就是?ZIP?文件,所以可以使用常見的一些解壓?ZIP?文件的工具來(lái)解壓?JAR?文件,如?Windows?下的?WinZip、WinRAR?等和?Linux?下的?unzip?等。使用?WinZip?和?WinRAR?等來(lái)解壓是因?yàn)樗鼈兘鈮罕容^直觀,方便。而使用?unzip,則是因?yàn)樗鈮簳r(shí)可以使用?-d?參數(shù)指定目標(biāo)目錄。?
在解壓一個(gè)?JAR?文件的時(shí)候是不能使用?jar?的?-C?參數(shù)來(lái)指定解壓的目標(biāo)的,因?yàn)?-C?參數(shù)只在創(chuàng)建或者更新包的時(shí)候可用。那么需要將文件解壓到某個(gè)指定目錄下的時(shí)候就需要先將這具?JAR?文件拷貝到目標(biāo)目錄下,再進(jìn)行解壓,比較麻煩。如果使用?unzip,就不需要這么麻煩了,只需要指定一個(gè)?-d?參數(shù)即可。如:?
unzip?test.jar?-d?dest/?
2)?使用?WinZip?或者?WinRAR?等工具創(chuàng)建?JAR?文件?
上面提到?JAR?文件就是包含了?META-INF/MANIFEST?的?ZIP?文件,所以,只需要使用?WinZip、WinRAR?等工具創(chuàng)建所需要?ZIP?壓縮包,再往這個(gè)?ZIP?壓縮包中添加一個(gè)包含?MANIFEST?文件的?META-INF?目錄即可。對(duì)于使用?jar?命令的?-m?參數(shù)指定清單文件的情況,只需要將這個(gè)?MANIFEST?按需要修改即可。?
3)?使用?jar?命令創(chuàng)建?ZIP?文件?
有些?Linux?下提供了?unzip?命令,但沒有?zip?命令,所以需要可以對(duì)?ZIP?文件進(jìn)行解壓,即不能創(chuàng)建?ZIP?文件。如要?jiǎng)?chuàng)建一個(gè)?ZIP?文件,使用帶?-M?參數(shù)的?jar?命令即可,因?yàn)?-M?參數(shù)表示制作?JAR?包的時(shí)候不添加?MANIFEST?清單,那么只需要在指定目標(biāo)?JAR?文件的地方將?.jar?擴(kuò)展名改為?.zip?擴(kuò)展名,創(chuàng)建的就是一個(gè)不折不扣的?ZIP?文件了,如將上一節(jié)的第?3)?個(gè)例子略作改動(dòng):?
jar?cvfM?test.zip?test
|
|