Java Archive - jarファイル

「.jar」の拡張子のファイルは、コンパイル済みの複数の.classファイルをZIP圧縮してアーカイブにしたものです。まとまった機能をひとつのファイルで持ち運べるので、配布に便利です。jarファイルには、classファイル以外にも画像など必要なりソースを含めることができます。
jarファイルは、圧縮した状態のまま中のクラスをプログラムから呼び出して使うことができます。また、実行エントリのmainメソッドが含まれる場合は、jarファイルそのものをプログラムとして実行できます。

簡単なjarファイルの作成と利用の手順

次のような2つのjavaソースコードがあるとします。そのうちの1つはmainメソッドがあります。これらをまとめて圧縮したtestjar.jarを作成します。
TestJarMain.java
class TestJarMain {
    public static void main(String args[]) {
        System.out.println("Main");
    }
}
TestJar.java
class TestJar {
    void message() {
        System.out.println("Hello");
    }
}
まずそれぞれをコンパイルして.classファイルを作ります。
$ javac TestJarMain.java
$ javac TestJar.java
TestJarMain.classとTestJar.classを「jarコマンド」でまとめて圧縮し、「testjar.jar」を作成します。
$ jar -cvf testjar.jar TestJar.class TestJarMain.class

マニフェストが追加されました
TestJar.classを追加中です(入=739)(出=527)(28%収縮されました)
TestJarMain.classを追加中です(入=420)(出=285)(32%収縮されました)
.classファイルは次のようにワイルドカードで一括してもかまいません。
$ jar -cvf testjar.jar *.class
コンパイルした2つのclassファイルは、jarにまとめて不要になったので削除します。
同じ場所に.classファイルが残っていると、jarファイルではなく.classファイルが使われます。
$ rm *.class
出来上がったtestjar.jarを利用してみます。testjar.jarからTestJarクラスを呼び出すTestクラスをTest.javaに実装します。
Test.java
class Test {
    public static void main(String args[]) {
        TestJar j = new TestJar();
        j.message();
    }
}
Test.javaをコンパイルします。
このとき、利用するjarファイルを「-classpath」オプションで指定します。-classpathはライブラリのクラスを探す場所を指定するもので、ディレクトリやjarファイルを指定します。これはコンパイラがjarファイルに圧縮保管されたTestJar.classを探すために必要です。
$ javac -classpath testjar.jar test.java
Testクラスを実行します。
このときは、jarファイルとカレントディレクトリを示す「.」とjarファイルを「-classpath」で指定します。実行に必要なクラスを探すために、testjar.jarはコンパイル時と同様に指定が必要で、加えてTest.classを探す場所としてカレントディレクトリも指定が必要です。
$ java -classpath .:testjar.jar Test
Hello
-classpathに複数の場所(jarファイル)を指定するときは「:(コロン)」で区切ります。
Windowsの場合は「:(コロン)」ではなく「;(セミコロン)」を区切り文字とします。Windowsでは「:」は「C:」のようなドライブ文字の指定に使われるからです。
-classpathオプションは、そのままCLASSPATH環境変数に設定してもかまいません。ただし環境変数に指定した場合、全てのコンパイルと実行に一律に適用されるので注意が必要です(-classpathオプションの使用が推奨されています)。
jarコマンドのオプション
jarコマンドはtarコマンドに似ており、次のようなオプションがあります。
-c 新規作成
-v 処理内容を表示する
-f jarファイル名
-t 一覧表示
-x jarファイル展開
-u jarファイル更新
-m マニフェストファイル
これらから主に次の組み合わせで使われます。
jar -cvf xxxx.jar xxx.class xxx.class ...
jar -cvf xxxx.jar *.class
jar -cvf xxxx.jar directory
新規作成(vは詳細表示なので任意)
jar -cvfm xxxx.jar xxx.mf *.class マニフェストファイルを指定して新規作成
jar -tf xxxx.jar-f jarファイルの構成ファイルを表示
jar -xvf xxxx.jar jarファイルを展開
ここでの例では全般に「v」オプションを使っていますが、詳細表示が不要なら省略できます。
ここで作成したtestjar.jarの内容を表示してみます。
$ jar -tf testjar.jar
META-INF/
META-INF/MANIFEST.MF
TestJar.class
TestJarMain.class
その中に、MANIFEST.MFというファイルが存在します。これは「マニフェストファイル」で、バージョン情報、リソース情報、メインクラス、CLASSPATH、セキュリティ情報などを格納します。マニフェストファイルはjarコマンドが自動的に作成します。ユーザがマニフェストに設定を与えることもできます。
MANIFEST.MF
Manifest-Version: 1.0
Created-By: 11.0.16 (Ubuntu)
jarファイルはxオプションにより展開できます。
$ jar -xvf testjar01.jar
  META-INF/が作成されました
 META-INF/MANIFEST.MFが展開されました
 TestJar.classが展開されました
 TestJarMain.classが展開されました

jarファイルの実行

testjar.jarに含めたTestJarMainにはmainメソッドがあります。jarファイルに含まれる実行エントリのmainメソッドを呼び出すことにより、jarファイルそのものを実行させることができます。
それには、jarファイルの中に生成されるMANIFEST.MF(マニフェストファイル)に、mainメソッドを持つクラス名を記述しておく必要があります。マニフェストに設定を追加するには、追加する設定を記載したマニフェストファイルを用意します。ここではmani.mfとします(このファイル名は任意です)。
次のように、mani.mfへ「Main-Class:」とmainメソッドを持つクラス名を記述します。「Main-Class:」の後ろは一つ空白を入れなければなりません。更にその行は改行しなければなりません(次の例の末行が空いているのはそういう意味)。
mani.mf
Main-Class: TestJarMain

mani.mfの設定を自動生成されるマニフェストファイルに追加するには、jarコマンドに「m」オプションを指定し、
    jar -cvfm xxx.jar manifest zzz.class
のようにマニフェストファイルを指定してjarを作成します。
$ javac TestJar.java
$ javac TestJarMain.java
$ jar -cvfm testjar.jar mani.mf *.class
マニフェストが追加されました
TestJar.classを追加中です(入=391)(出=278)(28%収縮されました)
TestJarMain.classを追加中です(入=420)(出=285)(32%収縮されました)
用意したmani.mfは、ファイル名が何であろうとMETA-INFディレクトリのMANIFEST.MFとして再生成され、Main-Class以外の必要な情報が自動的に追加されます。
MANIFEST.MF
Manifest-Version: 1.0
Main-Class: TestJarMain
Created-By: 11.0.16 (Ubuntu)
このjarファイルは、次のように直接javaコマンドに「-jar xxx.jar」と指定するだけでmainメソッドを呼び出して実行できます。
$ java -jar testjar.jar
Main

ディレクトリ階層を格納するjarファイル

jarファイルを作るとき、実際はパッケージに分けてディレクトリ階層になっていることが多いと思います。例えば、以下のような「testdir」ディレクトリ以下の階層に格納されているとします。
testdir/
    TestJarMain.class
    msg/
        TestJar.class
それぞれのjavaソースにはpackageでパッケージを宣言します。
TestJarMain.java
package testdir;

class TestJarMain {
    public static void main(String args[]) {
        System.out.println("Main");
    }
}
TestJar.java
package testdir.msg;

public class TestJar {
    public void message() {
        System.out.println("Hello");
    }
}
マニフェストには、mainメソッドを含むクラスをパッケージを考慮して指定します。
mani.mf
Main-Class: testdir.TestDirJarMain

jarコマンドにディレクトリを指定することで、ディレクトリ階層ごとjarファイルに取り込まれます。
$ jar -cvfm testdir.jar mani.mf testdir

マニフェストが追加されました
testdir/を追加中です(入=0)(出=0)(0%格納されました)
testdir/TestJarMain.classを追加中です(入=428)(出=288)(32%収縮されました)
testdir/msg/を追加中です(入=0)(出=0)(0%格納されました)
testdir/msg/TestJar.classを追加中です(入=403)(出=285)(29%収縮されました)
ディレクトリは、jarにまとめたので削除します。
$ rm -rf testdir
testdir.jarを使うTestクラスを次のように実装します。TestJarクラスはパッケージ階層を指定しています(from/importで宣言すれば省略できます)。
Test.java
class Test {
    public static void main(String args[]) {
        testdir.msg.TestJar j = new testdir.msg.TestJar();
        j.message();
    }
}
上の例と同様にjarファイルを-classpathで指定してコンパイルします。
$ javac -classpath testdir.jar Test.java
Testクラスを実行します。
$ java -classpath .:testdir.jar Test
Hello
testdir.jarを直接実行します。
$ java -classpath .:testdir.jar -jar testdir.jar
Main