JAR 文件格式以流行的 ZIP 文件格式為基礎,用於將許多文件聚集為一個文件。與 ZIP 文件不同,JAR 文件不僅用於壓縮和發布,還用於部署和封裝庫、組件和插件程序,並可被編譯器和 JVM 等工具直接使用。JAR 中包含特殊文件,如 manifests 和部署描述符,用來指示工具如何處理特定 JAR。
一個 JAR 文件可用於:
JAR 格式提供許多優勢和功能,許多是傳統壓縮格式如 ZIP 或 TAR 所無:
jar 工具預設壓縮文件。未壓縮 JAR 一般比壓縮 JAR 載入更快(載入過程中需解壓縮),但未壓縮文件網絡下載時間可能更長。
大多數 JAR 包含 META-INF 目錄,存儲包和擴展配置數據,如安全性和版本信息。Java 2 平台識別並解釋其中文件和目錄,以配置應用、擴展和類加載器:
MANIFEST.MF:定義與擴展和包相關數據。INDEX.LIST:由 jar -i 生成,包含應用或擴展中定義包的位置信息,是 JarIndex 實現一部分,由類加載器用於加速類加載。xxx.SF:JAR 簽名文件,xxx 標識簽名者。xxx.DSA:與簽名文件相關聯的簽名程序塊文件,存儲用於簽名 JAR 的公共簽名。執行基本任務用 Java Archive Tool(jar 工具),作為 JDK 一部分提供。用 jar 命令調用。常見用法:
| 功能 | 命令 |
|---|---|
| 用單個文件創建 JAR | jar cf jar-file input-file... |
| 用目錄創建 JAR | jar cf jar-file dir-name |
| 創建未壓縮 JAR | jar cf0 jar-file dir-name |
| 更新 JAR | jar uf jar-file input-file... |
| 查看 JAR 內容 | jar tf jar-file |
| 提取 JAR 內容 | jar xf jar-file |
| 提取特定文件 | jar xf jar-file archived-file... |
| 運行可執行 JAR 應用 | java -jar app.jar |
可執行 JAR 是自包含 Java 應用,存儲在特別配置的 JAR 中,可由 JVM 直接執行無需提取文件或設置類路徑。運行非可執行 JAR 中應用,必須加入類路徑並用名字調用主類。但用可執行 JAR,無需提取或知道主要入口點即可運行應用,有助於方便發布和執行。
將所有應用代碼放到一目錄。假設主類為 com.mycompany.myapp.Sample。創建包含應用代碼的 JAR 並標識主類:在某位置(非應用目錄)創建 manifest 文件,加入:
Main-Class: com.mycompany.myapp.Sample
然後創建 JAR:
jar cmf manifest ExecutableJar.jar application-dir
現在可用 java -jar ExecutableJar.jar 執行。可執行 JAR 必須通過 manifest 文件頭引用所需所有從屬 JAR。若使用 -jar 選項,環境變量 CLASSPATH 和命令行指定類路徑都被 JVM 忽略。
java -jar ExecutableJar.jar
密封 JAR 中包意味著包中定義所有類必須在同一 JAR 中找到,增強版本一致性,提供防止代碼篡改手段。要密封包,在 JAR manifest 為包添加 Name 頭,再加值為 "true" 的 Sealed 頭。創建 JAR 時指定具有適當頭元素的 manifest 文件密封 JAR:
Name: com/samplePackage/
Sealed: true
Name 頭標識包相對路徑名,以 "/" 結束以與文件名區別。Name 頭後第一個空行前所有頭作用於 Name 頭指定文件或包。上例中 Sealed 頭出現在 Name 後且中間無空行,故只應用於 com/samplePackage 包。若嘗試從密封包所在 JAR 以外其他地方加載密封包中類,JVM 將拋出 SecurityException。
擴展為 Java 平台增加功能,JAR 格式已加入擴展機制。擴展機制使 JAR 可通過 manifest Class-Path 頭指定所需其他 JAR。假設 extension1.jar 和 extension2.jar 同目錄,extension1.jar manifest 含:
Class-Path: extension2.jar
表明 extension2.jar 中類是 extension1.jar 中類的擴展類。extension1.jar 中類可調用 extension2.jar 中類,不要求 extension2.jar 在類路徑中。加載使用擴展機制 JAR 時,JVM 高效自動將 Class-Path 頭引用 JAR 添加到類路徑。但擴展 JAR 路徑被解釋為相對路徑,一般擴展 JAR 必須存儲在引用它的 JAR 同目錄。
例:ExtensionClient 引用 ExtensionDemo,捆綁在 ExtensionClient.jar,ExtensionDemo 捆綁在 ExtensionDemo.jar。為使 ExtensionDemo.jar 成擴展,必須將其列在 ExtensionClient.jar manifest Class-Path 頭中:
Manifest-Version: 1.0
Class-Path: ExtensionDemo.jar
Class-Path 值無路徑 ExtensionDemo.jar,表明與 ExtensionClient JAR 同目錄。
JAR 可用 jarsigner 工具或直接通過 java.security API 簽名。簽名 JAR 與原 JAR 完全相同,只是更新 manifest,在 META-INF 增加簽名文件和簽名塊文件。JAR 用存儲在 Keystore 數據庫中的證書簽名。Keystore 中證書有密碼保護,必須向 jarsigner 提供密碼才能簽名。

圖1 Keystore 數據庫
JAR 每位簽名者由 META-INF 中 .SF 擴展名簽名文件表示。格式類似 manifest — 一組 RFC-822 頭。組成包括主要部分(簽名者提供信息、不針對特定 JAR 項)和一系列單獨項(必須包含在 manifest 中)。驗證簽名 JAR 時,將簽名文件摘要值與 JAR 中相應項計算摘要值比較。
Contents of signature file META-INF/MANIFEST.MF
Manifest-Version: 1.0
Created-By: 1.3.0 (Sun Microsystems Inc.)
Name: Sample.java
SHA1-Digest: 3+DdYW8INICtyG8ZarHlFxX0W6g=
Name: Sample.class
SHA1-Digest: YJ5yQHBZBJ3SsTNcHJFqUkfWEmI=
Contents of signature file META-INF/JAMES.SF
Signature-Version: 1.0
SHA1-Digest-Manifest: HBstZOJBuuTJ6QMIdB90T8sjaOM=
Created-By: 1.3.0 (Sun Microsystems Inc.)
Name: Sample.java
SHA1-Digest: qipMDrkurQcKwnyIlI3Jtrnia8Q=
Name: Sample.class
SHA1-Digest: pT2DYby8QXPcCzv2NwpLxd8p4G4=
數字簽名是 .SF 簽名文件已簽名版本。數字簽名文件是二進制文件,與 .SF 同文件名但擴展名不同。根據數字簽名類型(RSA、DSA、PGP)及簽名 JAR 證書類型有不同擴展名。
簽名 JAR 必須先有私鑰。私鑰及相關公鑰證書存儲在名為 keystores 的密碼保護數據庫中。JDK 包含創建和修改 keystores 工具。keystore 中每個鑰可用別名標識,通常是擁有鑰簽名者名字。所有 keystore 項(鑰和信任證書項)用唯一別名訪問。別名在用 keytool -genkey 生成鑰對並添加項時指定。後續 keytool 命令必須用同樣別名引用此項。
例:用別名 "james" 生成新公鑰/私鑰對並將公鑰包裝到自簽名證書:
keytool -genkey -alias james -keypass jamespass
-validity 80 -keystore jamesKeyStore
-storepass jamesKeyStorePass
指定初始密碼 "jamespass",後續命令訪問 keystore "jamesKeyStore" 中別名 "james" 相關私鑰需此密碼。若 keystore 不存在,keytool 自動創建。
jarsigner 使用 keystore 生成或驗證 JAR 數字簽名。假設創建了 keystore "jamesKeyStore",含別名 "james" 鑰,簽名 JAR 命令:
jarsigner -keystore jamesKeyStore -storepass jamesKeyStorePass
-keypass jamespass -signedjar SSample.jar Sample.jar james
用密碼 "jamesKeyStorePass" 從 "jamesKeyStore" 提取別名 "james"、密碼 "jamespass" 鑰,對 Sample.jar 簽名、創建簽名 JAR SSample.jar。驗證簽名 JAR 更簡單:
jarsigner -verify SSample.jar
若簽名 JAR 未被篡改,jarsigner 會告知 JAR 通過驗證;否則拋出 SecurityException,指明哪些文件未通過驗證。也可用 java.util.jar 和 java.security API 編程簽名,或用 Netscape Object Signing Tool 等工具。
若應用或 applet 捆綁到多個 JAR,類加載器用簡單線性搜索算法搜索類路徑每個元素,可能下載並打開許多 JAR 直到找到所需類或資源。若類加載器尋找不存在資源,應用或 applet 中所有 JAR 都會下載。對大型網絡應用和 applet,導致啟動緩慢、響應遲緩、浪費帶寬。
JDK 1.3 後,JAR 格式支持索引優化網絡應用中類搜索,特別是 applet。JarIndex 機制收集 applet 或應用中定義所有 JAR 內容,將信息存儲到第一個 JAR 索引文件中。下載第一個 JAR 後,applet 類加載器使用收集內容信息高效加載 JAR。目錄信息存儲在根 JAR META-INF 目錄中 INDEX.LIST 簡單文本文件。
用 jar -i 選項創建。假設目錄結構如圖:

圖2 JarIndex
用命令為 JarIndex_Main.jar、JarIndex_test.jar、SampleDir/JarIndex_test1.jar 創建索引:
jar -i JarIndex_Main.jar JarIndex_test.jar SampleDir/JarIndex_test1.jar
INDEX.LIST 格式簡單,包含每個已索引 JAR 中包含包或類名字:
JarIndex-Version: 1.0
JarIndex_Main.jar
sp
JarIndex_test.jar
Sample
SampleDir/JarIndex_test1.jar
org
org/apache
org/apache/xerces
org/apache/xerces/framework
org/apache/xerces/framework/xml4j
JAR 格式遠超壓縮格式,有許多可改進效率、安全性和組織 Java 應用功能。因這些功能建立在核心平台(含編譯器和類加載器)中,開發人員可利用 JAR 格式能力簡化和改進開發部署過程。