Semakin besar proyek Java, semakin banyak class yang kita tulis, dan lama-lama satu folder berisi ratusan berkas .java dengan nama yang saling berdesakan. Java mengatasinya lewat package — mekanisme pengelompokan class yang berfungsi sebagai ruang nama (namespace) sekaligus batas kendali akses. Setiap class yang kita tulis selalu berada di dalam sebuah package, meskipun kita belum pernah mendeklarasikannya secara eksplisit. Pada Chapter ini kita akan belajar mendeklarasikan package, memahami hubungan package dengan struktur direktori, dan mengenali jebakan default package.
Package sebagai Ruang Nama #
Package memberi setiap class sebuah nama lengkap yang disebut fully qualified name. Class String milik pustaka standar misalnya bernama lengkap java.lang.String: package-nya java.lang, nama sederhananya String. Dengan ruang nama, dua class boleh bernama sama — com.toko.produk.Produk dan com.gudang.stok.Produk adalah dua class berbeda yang hidup berdampingan tanpa bertabrakan. Package juga menjadi dasar access control: modifier seperti public dan package-private hanya bermakna jika kita tahu class pemanggil berada di package yang sama atau berbeda — topik yang akan kita bedah tuntas pada Chapter Access Control Antarpaket.
Deklarasi package ditempatkan di baris pertama berkas sumber, sebelum semua deklarasi import dan class:
package com.toko.produk;
public class Produk {
// isi class
}Deklarasi Package dan Struktur Direktori #
Aturan utamanya sederhana namun wajib: nama package harus cocok dengan lokasi direktori berkas sumbernya. Class dengan deklarasi package com.toko.produk; harus berada di berkas com/toko/produk/Produk.java relatif terhadap akar sumber proyek. Compiler javac dan launcher java memakai aturan ini untuk menemukan class. Perhatikan struktur proyek kecil berikut:
src/
└── com/
└── toko/
└── produk/
├── Produk.java
└── AplikasiBelanja.javapackage com.toko.produk;
public class Produk {
private final String nama;
private final int harga;
public Produk(String nama, int harga) {
this.nama = nama;
this.harga = harga;
}
public String nama() {
return nama;
}
public int harga() {
return harga;
}
}package com.toko.produk;
public class AplikasiBelanja {
public static void main(String[] args) {
Produk kopi = new Produk("Kopi Robusta", 45000);
Produk gula = new Produk("Gula Pasir", 14000);
int total = kopi.harga() + gula.harga();
System.out.println(kopi.nama() + " : Rp" + kopi.harga());
System.out.println(gula.nama() + " : Rp" + gula.harga());
System.out.println("Total : Rp" + total);
}
}Karena kedua class berada di package yang sama, AplikasiBelanja memakai nama sederhana Produk tanpa import. Mari kita kompilasi kedua berkas dengan opsi -d lalu jalankan hasilnya:
javac -d kelas src/com/toko/produk/*.java
java -cp kelas com.toko.produk.AplikasiBelanjaOutput yang Dihasilkan:
Kopi Robusta : Rp45000
Gula Pasir : Rp14000
Total : Rp59000Mari kita telaah perintah dan baris kuncinya:
- Baris 1
package com.toko.produk;: memberi tahu compiler bahwa class di berkas ini termasuk packagecom.toko.produk, sehingga berkas wajib berada dicom/toko/produk/. - Perintah
javac -d kelas ...: opsi-dmenyuruh compiler meletakkan berkas.classke direktorikelassambil membuat subdirektoricom/toko/produk/secara otomatis mengikuti nama package. - Perintah
java -cp kelas com.toko.produk.AplikasiBelanja: JVM mencari berkaskelas/com/toko/produk/AplikasiBelanja.class— nama class yang kita sebut diterjemahkan menjadi path berkas bytecode. - Baris
Produk kopi = new Produk(...): tanpa package yang sama, pemanggilan ini harus diganti import atau nama lengkap — pokok bahasan Chapter berikutnya.
Jika kita lupa memberi -cp kelas, JVM tidak menemukan class-nya dan mencetak Error: Could not find or load main class com.toko.produk.AplikasiBelanja — pesan yang sangat sering muncul di awal perjalanan belajar Java.
Kelas Selevel dalam Satu Direktori #
Bagaimana dengan contoh-contoh sejauh ini yang tidak mendeklarasikan package sama sekali? Class seperti itu berada di default package — package tanpa nama. Class-class selevel dalam direktori yang sama saling mengenali tanpa import, persis seperti class sepackage. Contoh berikut dapat Anda jalankan langsung dengan java Kasir.java; class Produk pendampingnya berada di berkas Produk.java pada direktori yang sama:
public class Produk {
private final String nama;
private final int harga;
public Produk(String nama, int harga) {
this.nama = nama;
this.harga = harga;
}
public String nama() {
return nama;
}
public int harga() {
return harga;
}
}public class Kasir {
public static void main(String[] args) {
Produk kopi = new Produk("Kopi Robusta", 45000);
Produk gula = new Produk("Gula Pasir", 14000);
int total = kopi.harga() + gula.harga();
System.out.println(kopi.nama() + " : Rp" + kopi.harga());
System.out.println(gula.nama() + " : Rp" + gula.harga());
System.out.println("Total : Rp" + total);
}
}Output yang Dihasilkan:
Kopi Robusta : Rp45000
Gula Pasir : Rp14000
Total : Rp59000Launcher java mengeksekusi Kasir.java, lalu mengompilasi Produk.java secara otomatis karena dibutuhkan oleh Kasir — pola multi-file source launch yang praktis untuk latihan.
Default Package dan Bahayanya #
Mengapa default package berbahaya? Pertama, tidak ada mekanisme import menuju class di dalamnya, sehingga kode ber-package tidak dapat menggunakan class tersebut — batasan satu arah yang membuat proyek tidak bisa tumbuh. Kedua, semua class selevel berbagi satu ruang nama tanpa nama, sehingga risiko nama bertabrakan antar bagian proyek menjadi nyata begitu jumlah berkas bertambah. Ketiga, default package tidak bisa menjadi bagian dari module bernama saat kita beralih ke Java Module System pada bab-bab akhir Section ini.
Kompilasi dengan Opsi -d #
Opsi -d pada javac menegaskan pemisahan dua hal yang sering tercampur: direktori sumber dan direktori keluaran. Tanpa -d, compiler menaruh Produk.class di sebelah berkas sumbernya; dengan -d kelas, seluruh struktur direktori com/toko/produk/ dibuat ulang di bawah kelas dan bytecode berkumpul di sana. Struktur inilah yang dibaca JVM melalui classpath, dan struktur inilah yang nanti dibungkus menjadi berkas JAR pada Chapter JAR Files.
Kita sekarang punya class yang teratur dalam package bernama. Pertanyaan berikutnya: bagaimana class com.toko.kasir.KasirApp memanggil class com.toko.produk.Produk yang berbeda package? Jawabannya adalah pernyataan import, topik Chapter berikutnya.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
Kasir.java
Unduh berkaspublic class Kasir {
public static void main(String[] args) {
Produk kopi = new Produk("Kopi Robusta", 45000);
Produk gula = new Produk("Gula Pasir", 14000);
int total = kopi.harga() + gula.harga();
System.out.println(kopi.nama() + " : Rp" + kopi.harga());
System.out.println(gula.nama() + " : Rp" + gula.harga());
System.out.println("Total : Rp" + total);
}
}
Produk.java
Unduh berkaspublic class Produk {
private final String nama;
private final int harga;
public Produk(String nama, int harga) {
this.nama = nama;
this.harga = harga;
}
public String nama() {
return nama;
}
public int harga() {
return harga;
}
}