網頁

2013年4月7日 星期日

Java Programming Introduction 04: JAR

這裡要介紹的是 Java 的 library 和 執行檔的格式 JAR. JAR 既是 Java 的 library, 也是 Java 的執行檔. 必要的時候, 一個 JAR 檔案, 可以同時是 library 和執行檔. 以下的內容, 說明它是怎麼做到的, 它的結構, 以及在 command line 下, 如何製作.
 

是檔案格式也是打包工具

JAR (Java ARchive) Java library 的檔案格式, 也是 Java 打包 JAR 檔案的工具, 附在 JDK 裡面.

JAR 檔案 它的副檔名通常是 .jar, 雖然並沒有規定一定要用這樣的副檔名, 官方文件上也特別提到可以用其他的副檔名, 但是變更副檔名似乎並沒有好處.

.jar 的內容, 可以包含 .class (編譯過的 java byte code), .java (java 的原始碼), 或是其他的 .jar, 以及 resource (text, image, ...).

事實上, .jar 就是一個 zip . 如果把它 rename .zip, 可以把它解壓縮出來. 無論和 java 有無關係, 都可以放到 .jar . 就算是一個 windows 的執行程式, .exe, 也可以用 jar 這個工具程式, 把它放到 .jar 檔案中.

package

雖然大部份的文件, 在介紹 jar , 都不會提到 package, 但是如果沒有 package, 就算打包成 jar 也無法使用.

package Java 裡面, 是一組 class 的合集, class 之間, 並不一定要有關係, 只是放在一起而已. 一個 Java 程式, 可能包含或引用的 class 可能有數十或數百個, 名稱衝突就成了一個很大的問題. 為了解決這個問題, 引用了樹狀的命名結構. 譬如說, Red Face 這個公司的 excel library 就可以採用 com.redface.excel 作為 package 的命名. 而這個 package 中的 File, Worksheet, Page class, 就可以命名為 com.redface.excel.File, com.redface.excel.Worksheet, .... 這樣就不會和 java.util.io.File 這個 class 衝突. 就可以解決命名的問題. 除非 Red Face 這個公司的網址有變動, 或是公司的內控有問題, 否則不會有命名衝突的問題.

因為 package 解決了命名的問題, 所以 .jar 可以把亂七八糟的東西都放在一個檔案中.

How to make a jar from command line


我們用這個 java 程式, 來做例子, 把它放到 jar .

InJar.java


package jarlib;
public class InJar {
     public static void main(String[] args) {
           System.out.println("I'm in Jar");        
     }
 
     public void doIt(){
           System.out.println("InJar doing ..." );
     }
}

package jarlib;
這一個敘述, 說明 InJar 這個 class, 是在 jarlib package . 程式的其他部份都很簡單, 就不多說了.

javac -d . InJar.java
然後在 command line javac 來編譯它. -d 的意思是指定把編譯的結果 (.class) 檔案, 放到路徑中. 我們現在只需要放到目前目錄, 所以就是 .

編譯後, 我們可以發現, 在目前的路徑下, 多了一個 jarlib 的目錄. 我們編譯完成的InJar.class 就放在這裡面. 也就是說, 它會根據我們的 package 結構, 建立相對應的路徑. 如果我們在程式中的 pacakge 敘述, 修改成
package jarlib.jar.lib;
那麼建立出來的路徑, 就會是 jarlib/jar/lib

jar cf jarlib.jar jarlib

這一個 jar 的命令, create file, 建立 jarlib.jar, jarlib 路徑下的檔案, 都放到 jarlib.jar . 用解壓縮軟體, 例如 7-zip 可以直接解開這個 .jar

解開 jarlib.jar 這個檔案, 除了我們指定的 jarlib 的目錄, 還多了一個

META-INF 的目錄. 這個 META-INF 的目錄, 裡面只有一個檔案, MANIFEST.MF, 這個檔案的內容如下 :

Manifest-Version: 1.0
Created-By: 1.7.0_09 (Oracle Corporation)

當我們要製作可以執行的 jar, java 的執行環境, 就是從這裡找出要執行的 class 作為進入點.

How to use a jar from java code

我們需要另外一個程式來使用這個 jar 檔案.

Tester.java

import jarlib.InJar;
public class Tester {
     public static void main(String[] args) {
            System.out.println("I'm Tester");  
            InJar in_jar = new InJar();
            in_jar.doIt();
     }
}

javac -cp ./jarlib.jar Tester.java

編譯測試程式的時候, 需要指定含入的 library, 也就是 jar. 這裡是指定目前目錄(.) jarlib.jar.

以下命令的結果, 在這個例子中是相同的
javac -cp jarlib.jar Tester.java
javac -classpath jarlib.jar Tester.java
javac -classpath ./jarlib.jar Tester.java

java -cp .;jarlib.jar Tester
接下來執行 Tester 這個測試程式. 這邊需要把 jarlib.jar 插入 class path, 這邊的語法, 和平台有關:

Windows :

 

java -cp .;jarlib.jar Tester

Linux & Mac :

java -classpath .:jarlib.jar Tester

 

輸出的結果如下

java -cp .;jarlib.jar Tester
I'm Tester
InJar doing ...

Create Executable Jar


要讓 Jar 變成可以執行, 需要在 Jar 中定義當這個 jar 被執行的時候, 它的進入點為何. 這邊我們需要定義一個 InJar.MF, 這個檔案的內容如下, 記得最後要多加一行空白 (多按一次 enter) 

Manifest-Version: 1.0
Main-Class: jarlib.InJar

把它放在 jarlib 這個目錄中. (javac -d . InJar.java 這個命令可以建立這個目錄, 並把 InJar.class 放在這個目錄中)

然後執行

jar cmf ./jarlib/InJar.MF jarlib.jar ./jarlib/InJar.class
就可以把 InJar.class 打包,變成一個可以執行的 jar

java -jar jarlib.jar
在執行 jar 的時候, 需要多加一個 -jar 參數, 然後就可以順利執行了. 打開 jarlib.jar, 可以看到裡面多了 MANIFEST.MF. MANIFEST.MF 的最後, 多了一行 Main-Class: jarlib.InJar, 這一行就是指定這一個 jar 的進入點, 當這一個 jar 被執行時, java VM 會在這個檔案中尋找 Main-Class, 再在 Main-Class 中尋找 main 這個 function, 並且執行.

Manifest-Version: 1.0
Created-By: 1.7.0_09 (Oracle Corporation)
Main-Class: jarlib.InJar