Saat proyek dikompilasi, hasilnya berupa pohon direktori .class yang dalam — kelas/com/toko/produk/Produk.class dan seterusnya. Membagikan pohon itu ke pengguna atau rekan tim berarti menyalin ratusan berkas kecil yang rawan terpotong path-nya. Java menuntaskannya dengan JAR (Java Archive) — satu berkas yang membungkus seluruh struktur direktori class beserta resources tambahan. Pada Chapter ini kita akan membedah isi JAR, membuatnya dengan tool jar, menjalankan aplikasi ber-JAR, dan memakai JAR library lewat classpath.

JAR sebagai Arsip ZIP #

Secara format, JAR adalah berkas ZIP biasa yang mengikuti struktur direktori package. Anda bisa membuktikannya sendiri: berkas .jar dapat dibuka dengan tool ZIP mana pun. Yang membedakannya dari ZIP polos adalah direktori khusus META-INF/ di akarnya, terutama berkas manifest (META-INF/MANIFEST.MF) yang menyimpan metadata arsip — di antaranya entry Main-Class yang menandai class titik masuk aplikasi.

Kita akan memakai dua class kecil sepanjang chapter ini — aplikasi sapaan dan class utilitas pendampingnya:

java UtilMath.java
public class UtilMath {
    public static int kuadrat(int n) {
        return n * n;
    }
}
java SapaanApp.java
public class SapaanApp {
    public static void main(String[] args) {
        String nama = args.length > 0 ? args[0] : "Budi";
        int kuadrat = UtilMath.kuadrat(nama.length());

        System.out.println("Halo, " + nama + "!");
        System.out.println("Panjang nama dikuadratkan: " + kuadrat);
    }
}

Output yang Dihasilkan:

text
Halo, Budi!
Panjang nama dikuadratkan: 16

Programnya sederhana, tetapi dua class ini cukup untuk mendemonstrasikan seluruh siklus hidup JAR — dari kompilasi hingga distribusi.

Membuat dan Menjalankan JAR Aplikasi #

Alurnya tiga langkah: kompilasi, bungkus dengan jar --create, lalu jalankan. Perintah berikut kita jalankan sungguhan di direktori kerja yang memuat kedua class di atas:

bash Kompilasi lalu bungkus menjadi JAR aplikasi
javac SapaanApp.java UtilMath.java
jar --create --main-class=SapaanApp --file sapaan-app.jar SapaanApp.class UtilMath.class
java -jar sapaan-app.jar Siti

Output yang Dihasilkan:

text
Halo, Siti!

Mari kita bedah perintah dan hasilnya:

  1. javac SapaanApp.java UtilMath.java: mengompilasi kedua berkas menjadi SapaanApp.class dan UtilMath.class di direktori kerja.
  2. jar --create --main-class=SapaanApp --file sapaan-app.jar ...: tool jar membuat arsip sapaan-app.jar berisi kedua berkas .class; opsi --main-class menuliskan entry Main-Class: SapaanApp ke manifest sehingga JAR tahu titik masuknya.
  3. java -jar sapaan-app.jar Siti: launcher membaca manifest, mencari Main-Class, memuat SapaanApp, dan mengalirkan argumen Siti ke args — perhatikan sapaan menyapa Siti, bukan nilai bawaan Budi.

Isi manifest-nya bisa kita lihat langsung dengan mengekstrak berkasnya dari arsip:

bash Membaca manifest dari dalam JAR
unzip -p sapaan-app.jar META-INF/MANIFEST.MF

Output yang Dihasilkan:

text
Manifest-Version: 1.0
Created-By: 25.0.4.1 (Eclipse Adoptium)
Main-Class: SapaanApp

Tiga baris itu seluruh mekanisme di balik java -jar: versi manifest, pembuat arsip, dan nama class utama. Baris manifest ditutup dengan carriage return + line feed khas format ini, dan berkas selalu diakhiri satu baris kosong.

JAR Library di Classpath #

JAR tanpa Main-Class berperan sebagai library: kumpulan class untuk dipakai program lain. Launcher tetap memerlukannya melalui opsi classpath -cp. Mari bungkus ulang kedua class tanpa opsi main, lalu jalankan class dari dalamnya:

bash Membuat JAR library dan memakainya via classpath
jar --create --file sapaan.jar SapaanApp.class UtilMath.class
java -cp sapaan.jar SapaanApp Dewi

Output yang Dihasilkan:

text
Halo, Dewi!

Opsi -cp sapaan.jar menyuruh JVM mencari class di dalam arsip — launcher menemukan SapaanApp di entry SapaanApp.class, memuatnya, dan menjalankan main. Inilah cara klasik mendistribusikan library ke pengguna: satu berkas JAR yang ditambahkan ke classpath proyek mereka. Bila class yang kita bungkus ber-package, struktur direktorinya ikut terbawa — com/toko/produk/Produk.class tersimpan di dalam JAR persis seperti struktur hasil javac -d.

Memeriksa Isi dengan jar –list #

Sebelum memakai JAR dari orang lain, sering kita perlu tahu isinya. Opsi --list (bentuk pendeknya jar tf) mencetak seluruh entry arsip:

bash Melihat isi JAR aplikasi
jar --list --file sapaan-app.jar

Output yang Dihasilkan:

text
META-INF/
META-INF/MANIFEST.MF
SapaanApp.class
UtilMath.class

Daftar ini memperlihatkan anatomi JAR secara utuh: direktori META-INF/ dengan manifest-nya diikuti berkas class di akar arsip — sesuai class tanpa package. JAR ber-package akan menampilkan entry seperti com/toko/produk/Produk.class. Pemeriksaan semacam ini menjawab banyak kasus “class tidak ditemukan” karena kita bisa memastikan class yang dicari memang ada di dalam arsip, dengan path yang persis sama dengan nama package-nya.

Kita kini punya satu berkas yang rapi untuk dibagikan. Namun classpath sendiri menyimpan masalah lama: dua JAR membawa class bernama sama, versi yang bertabrakan, dan tidak ada cara tahu dependensi apa saja yang sebenarnya dibutuhkan JAR milik orang lain. Ketidakpastian inilah yang memicu lahirnya Java Module System — topik Chapter berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

SapaanApp.java

Unduh berkas
java
public class SapaanApp {
    public static void main(String[] args) {
        String nama = args.length > 0 ? args[0] : "Budi";
        int kuadrat = UtilMath.kuadrat(nama.length());

        System.out.println("Halo, " + nama + "!");
        System.out.println("Panjang nama dikuadratkan: " + kuadrat);
    }
}

UtilMath.java

Unduh berkas
java
public class UtilMath {
    public static int kuadrat(int n) {
        return n * n;
    }
}