Menerapkan pilar pertama OOP: access modifiers public, protected, package-default dan private, lalu pola encapsulation dengan field private plus getter/setter, object immutable berbasis final, dan JavaBeans pattern yang dipakai framework di dunia nyata.

Di episode 4 dan 5 kita membuat object dengan field yang terbuka — siapa pun bisa langsung menulis mobil.warna = "biru". Episode ini mengubah itu. Kita masuk ke pilar pertama OOP: encapsulation — menyembunyikan detail internal dan hanya membuka akses yang aman.
Mengapa encapsulation penting? Karena field publik adalah undangan bug. Siapa pun bisa memasukkan nilai tidak valid (harga = -500), dan begitu object rusak, sulit menemukan siapa yang merusaknya. Encapsulation bukan hanya "menyembunyikan", tetapi menjaga invarian — memastikan object tidak pernah masuk ke keadaan yang tidak sah.
Empat tingkatan akses di Java, dari paling terbuka ke paling ketat:
| Modifier | Akses dari | Kapan dipakai |
|---|---|---|
public | Semua class di mana pun | API publik yang aman diekspos |
protected | Package yang sama + subclass | API untuk pewaris (episode 7) |
| (default/package) | Hanya package yang sama | Implementasi internal package |
private | Hanya class itu sendiri | Detail internal, state sejati |
public class Contoh {
public int untukSemua;
protected int untukTurunan;
int untukPackage;
private int rahasia;
}Analogi yang mudah: rumah. public adalah halaman depan (semua orang boleh masuk), protected adalah kamar keluarga (keluarga + tamu tertentu), default adalah ruang tamu (yang tahu alamatnya), private adalah kamar pribadi (hanya pemiliknya).
private. Hampir selalu.public jika bagian dari API, private jika helper internal.private), lalu longgarkan hanya jika ada kebutuhan nyata.Mengapa protected dan default jarang dipakai di codebase modern? Karena keduanya melanggar "segel" encapsulation tanpa keuntungan yang jelas. Bahasa seperti Rust dan Go bahkan tidak punya konsep protected — sinyal bahwa akses lintas class seharusnya disengaja, bukan karena kebetulan.
Enkapsulasi = field private + method akses terkontrol:
public class Produk {
private String nama;
private double harga;
public String getNama() {
return nama;
}
public void setNama(String nama) {
this.nama = nama;
}
public double getHarga() {
return harga;
}
public void setHarga(double harga) {
if (harga < 0) {
throw new IllegalArgumentException("Harga tidak boleh negatif");
}
this.harga = harga;
}
}Perhatikan setHarga: ia memvalidasi sebelum menyimpan. Field tidak bisa diisi -500 karena setter menolaknya. Inilah wujud nyata "menjaga invarian" — akses tidak dibuka langsung, melainkan melalui gerbang yang berisi aturan.
Jika getter/setter cuma memakai kembali field, kenapa tidak langsung public saja? Tiga alasan:
double ke BigDecimal) tanpa memecah pemanggil, karena API getter/setter tetap.Inilah kunci konseptual: getter/setter adalah kontrak, field adalah detail implementasi. Pemanggil bergantung pada kontrak, bukan pada field.
finalKadang object justru tidak perlu diubah sama sekali. Immutability — object yang tidak bisa berubah setelah dibuat — menghilangkan seluruh kelas bug: race condition, perubahan tidak sengaja, dan state yang tidak konsisten.
public final class Titik {
private final int x;
private final int y;
public Titik(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}Aturan immutable object:
private final — diset sekali di constructor, tidak pernah berubah.final agar tidak bisa di-subclass (mencegah subclass menambah state yang bisa diubah).List), jangan ekspos reference aslinya — kembalikan salinan (defensive copy, episode 18).Bandingkan dengan object mutable: Titik immutable tidak bisa "rusak" setelah lahir. Setiap perubahan perlu membuat object baru:
Titik pindah = new Titik(titik.getX() + 5, titik.getY());Di episode 10, record akan memberi immutability ini secara gratis — dan di episode 18 kita akan lihat mengapa immutable menjadi best practice produksi.
JavaBeans adalah konvensi (bukan keyword) yang dipakai framework Java selama puluhan tahun:
private + getter/setter publik.setNamaProperti, getter getNamaProperti.public class User implements java.io.Serializable {
private String email;
private String nama;
public User() {}
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getNama() { return nama; }
public void setNama(String nama) { this.nama = nama; }
}JavaBeans lahir dari era reflection-heavy frameworks (JSP, Spring) yang butuh cara menciptakan object kosong lalu mengisi properti lewat setter. Kelemahannya kini jelas: object bisa berada dalam keadaan "setengah terisi". Karena itu Java modern lebih memilih constructor penuh (episode 5) atau record (episode 10) — object lahir lengkap, bukan diisi kemudian.
Warning
Jangan pernah menulis getter untuk field mutable lalu mengembalikan reference-nya langsung. Contoh: public List<String> getNama() { return listNama; } — pemanggil bisa memodifikasi list dari luar, merusak encapsulation. Kembalikan salinan atau view tak-ubah (Collections.unmodifiableList).
public class Rekening {
private final String pemilik;
private double saldo;
public Rekening(String pemilik, double saldoAwal) {
if (pemilik == null || pemilik.isBlank()) {
throw new IllegalArgumentException("Pemilik wajib diisi");
}
this.pemilik = pemilik;
this.saldo = Math.max(saldoAwal, 0);
}
public String getPemilik() {
return pemilik;
}
public double getSaldo() {
return saldo;
}
public void deposit(double nominal) {
if (nominal <= 0) {
throw new IllegalArgumentException("Nominal harus positif");
}
saldo += nominal;
}
}Perhatikan: pemilik final (tidak berubah), saldo hanya bisa berubah lewat deposit yang memvalidasi. Tidak ada jalan untuk membuat rekening "rusak" — invarian dijaga sejak constructor. Ini encapsulation + immutability bekerja bersama.
Tip
Jika setter kalian tidak melakukan validasi atau logika apa pun (hanya this.x = x), pertimbangkan untuk menghilangkan setter sama sekali dan jadikan field-nya final. Setter tanpa aturan hanyalah public field yang lebih bertele-tele — dan object immutable lebih aman.
Inti yang harus dibawa pulang:
public, protected, default (package), private — mulai dari yang paling ketat.private + akses terkontrol lewat getter/setter yang memvalidasi.final fields, tanpa setter, class final) menghilangkan seluruh kelas bug.record.Di episode 7 selanjutnya kita akan membahas inheritance — extends, super, method overriding, @Override, dan peran Object sebagai root class, plus pitfall favorit composition over inheritance. Sampai jumpa di episode 7!