Sepanjang section ini kita menjalankan alat langsung dari kode sumber — nyaman saat pengembangan, tetapi bukan bentuk yang bisa diserahkan ke pengguna: mesin sasaran mungkin tidak punya JDK. Packaging menutup jarak itu lewat tangga empat anak: kelas hasil kompilasi dibungkus JAR, JAR diberi titik masuk lewat manifest, aplikasi dirakit bersama runtime minimal menjadi image jlink, lalu dibungkus pemasang lewat jpackage. Chapter penutup section ini menaiki tangga itu dengan satu alat kecil yang sama.

Dari Kode Sumber ke JAR #

Alat paketan kita sengaja kecil: sapa menyapa satu nama dan melaporkan versinya. Seluruh konvensi CLI dari chapter Utilitas Command Line tetap berlaku — keluaran data ke stdout, keluhan ke stderr, exit code sebagai kontrak:

java Sapa.java
public class Sapa {
    static final String VERSI = "1.0";

    public static void main(String[] args) {
        if (args.length == 1 && args[0].equals("--versi")) {
            System.out.println("sapa " + VERSI);
            return;
        }
        if (args.length != 1) {
            System.err.println("Pemakaian: sapa NAMA | sapa --versi");
            System.exit(2);
        }
        System.out.println("Halo, " + args[0] + "!");
    }
}

Output yang Dihasilkan:

text
Halo, Budi!

Pemakaian yang salah tetap ditolak: tanpa argumen, program mencetak Pemakaian: sapa NAMA | sapa --versi ke stderr lalu keluar dengan exit code 2 — kontrak yang terbawa utuh ke semua bentuk kemasan berikutnya:

java Sapa.java
public class Sapa {
    static final String VERSI = "1.0";

    public static void main(String[] args) {
        if (args.length == 1 && args[0].equals("--versi")) {
            System.out.println("sapa " + VERSI);
            return;
        }
        if (args.length != 1) {
            System.err.println("Pemakaian: sapa NAMA | sapa --versi");
            System.exit(2);
        }
        System.out.println("Halo, " + args[0] + "!");
    }
}

Sesi berikut dikutip dari eksekusi sungguhan dengan JDK 25:

text
$ javac -d kelas Sapa.java
$ jar --create --file sapa.jar --main-class Sapa -C kelas .
$ jar --list --file sapa.jar
META-INF/
META-INF/MANIFEST.MF
Sapa.class
$ java -jar sapa.jar Budi
Halo, Budi!
$ java -jar sapa.jar --versi
sapa 1.0
$ java -cp sapa.jar Sapa Siti
Halo, Siti!

Mari kita telaah perintah-perintahnya:

  1. Baris javac -d kelas Sapa.java: compiler menaruh Sapa.class di direktori kelas/ — berkas bytecode inilah yang dibagikan, bukan sumbernya.
  2. Baris jar --create --file sapa.jar --main-class Sapa -C kelas .: alat jar membungkus isi kelas/ menjadi satu arsip; -C kelas . berarti “ambil semuanya dari dalam direktori kelas”, dan --main-class Sapa menuliskan atribut Main-Class ke manifest.
  3. Hasil jar --list: JAR hanyalah arsip zip berisi berkas .class plus dua entri khusus: direktori META-INF/ dan MANIFEST.MF di dalamnya.
  4. Manifest hasilnya berisi tiga baris — Manifest-Version: 1.0, Created-By: 25.0.4.1 (Eclipse Adoptium), dan Main-Class: Sapa; baris terakhir itulah yang dibaca java -jar.
  5. Baris java -jar sapa.jar Budi: peluncur membaca manifest, menjalankan Sapa.main, lalu meneruskan Budi sebagai argumen — pengguna tidak perlu tahu nama class di dalam arsip.
  6. Baris java -cp sapa.jar Sapa Siti: jalur tanpa manifest — classpath menyatakan letak kelas dan nama class ditulis eksplisit; berguna saat satu arsip berisi beberapa program.

JAR menghilangkan langkah kompilasi, tetapi masih menuntut java yang tepat terpasang di mesin pengguna. jlink melangkah lebih jauh: merakit aplikasi bersama bagian JDK yang dibutuhkan menjadi satu direktori image yang berjalan mandiri. Modul menuntut class berada di package bernama, jadi versi modular alat kita diletakkan di com/contoh/sapa/Sapa.java:

java com/contoh/sapa/Sapa.java — versi modular
package com.contoh.sapa;

public class Sapa {
    static final String VERSI = "1.0";

    public static void main(String[] args) {
        if (args.length == 1 && args[0].equals("--versi")) {
            System.out.println("sapa " + VERSI);
            return;
        }
        if (args.length != 1) {
            System.err.println("Pemakaian: sapa NAMA | sapa --versi");
            System.exit(2);
        }
        System.out.println("Halo, " + args[0] + "!");
    }
}
java module-info.java — nama modul sapa
module sapa {
    // java.base selalu tersedia secara implisit; tidak ada kebutuhan lain.
}

Sesi berikut juga dikutip dari eksekusi sungguhan:

text
$ javac -d modul src/module-info.java src/com/contoh/sapa/Sapa.java
$ jlink --add-modules sapa --module-path modul --output citra-sapa --launcher sapa=sapa/com.contoh.sapa.Sapa --strip-debug --no-header-files --no-man-pages
$ citra-sapa/bin/sapa Budi
Halo, Budi!
$ citra-sapa/bin/sapa --versi
sapa 1.0
$ citra-sapa/bin/java --list-modules
[email protected]
sapa

Mari kita telaah flag-flag jlink:

  1. Flag --module-path modul: lokasi modul aplikasi hasil javac.
  2. Flag --add-modules sapa: modul yang dirakit beserta seluruh dependensinya; karena sapa hanya membutuhkan java.base, image akhirnya berisi dua modul saja — terbukti pada keluaran --list-modules.
  3. Flag --launcher sapa=sapa/com.contoh.sapa.Sapa: membuat skrip peluncur bin/sapa — pengguna memanggilnya tanpa menyebut class, classpath, ataupun modul.
  4. Flag --strip-debug --no-header-files --no-man-pages: memangkas simbol debug dan berkas pengiring; image contoh ini berukuran ±56 MB — jauh lebih kecil daripada JDK utuh.

Pembungkus Pemasang dengan jpackage #

Anak tangga terakhir menyerahkan pengalaman pemasangan yang dikenal pengguna akhir — .deb, .rpm, .msi, atau .dmg — lewat jpackage, yang merakit image ala jlink, membungkusnya menjadi paket sistem operasi, dan menambahkan peluncur. Bentuk perintahnya:

bash Membangun pemasang deb dengan jpackage
jpackage --name sapa --input kelas --main-jar sapa.jar \
    --main-class Sapa --type deb --dest paket

Membangun paket .deb menuntut utilitas pembungkus Debian (fakeroot, dpkg-deb) di mesin build; .msi menuntut WiX di Windows, .dmg menuntut macOS. Detail flag lainnya ada di referensi akhir chapter.

Memilih Bentuk Distribusi #

BentukDibutuhkan penggunaCocok untuk
Sapa.java (kode sumber)JDK 25eksperimen, skrip internal
JAR + classpath (java -cp)JDKtim pengembang, beberapa program per arsip
JAR eksekusabel (java -jar)JDK/JRE 25+distribusi internal, entry cron
Image jlinktidak ada — runtime ikut terbawaserver, CLI lintas tim, satu platform
Pemasang jpackagetidak ada — ada proses pasangaplikasi desktop pengguna akhir

Satu batasan jar: alat bawaan JDK ini tidak menggabungkan dependency ke dalam satu arsip. Untuk pustaka pihak ketiga, build tool yang menyatukannya — Maven Shade atau Gradle Shadow menghasilkan fat jar (JAR berisi semua dependensi) dengan manifest Main-Class yang sama.

Section Scripting dan Automation ditutup di sini: eksperimen cepat di jshell, konvensi CLI, parsing argumen, batch dengan checkpoint, otomasi file system, job terjadwal, dan pengemasan untuk distribusi — satu alur utuh dari pertanyaan kecil menjadi artefak siap pakai. Section berikutnya, Proyek, mengajak memakai seluruh bekal itu untuk membangun aplikasi nyata dari nol sampai siap dibagikan.

Referensi #

Berkas Sumber

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

Sapa.java

Unduh berkas
java
public class Sapa {
    static final String VERSI = "1.0";

    public static void main(String[] args) {
        if (args.length == 1 && args[0].equals("--versi")) {
            System.out.println("sapa " + VERSI);
            return;
        }
        if (args.length != 1) {
            System.err.println("Pemakaian: sapa NAMA | sapa --versi");
            System.exit(2);
        }
        System.out.println("Halo, " + args[0] + "!");
    }
}