Sejak Chapter hierarki, kita menunda satu cabang: Error, saudara Exception di bawah parent Throwable. Perbedaan keduanya bukan soal nama, melainkan soal siapa yang salah dan siapa yang bisa memperbaikinya. Exception menggambarkan kegagalan yang terjadi di dalam logika program kita — masukan tidak sah, berkas hilang — sehingga program wajar menanggapinya. Error menggambarkan kondisi fatal di level JVM atau runtime sistem: stack pemanggilan habis atau memori tidak tersisa lagi. Pada Chapter ini kita amati StackOverflowError secara nyata, pahami mengapa cabang ini hampir selalu dibiarkan tidak ditangani, dan kenali batas tegas antara dua cabang tersebut.
Cabang Error dan Isinya #
Error adalah turunan langsung Throwable yang ditujukan untuk kegagalan serius yang tidak wajar dipulihkan oleh program aplikasi. Dua anggotanya yang paling terkenal:
StackOverflowError: stack pemanggilan method — wilayah memori yang menampung frame tiap pemanggilan — sudah penuh. Pemicu paling umum adalah rekursi tanpa kondisi dasar (base case), di mana method memanggil dirinya sendiri tanpa jalan berhenti.OutOfMemoryError: heap JVM tidak lagi bisa memenuhi permintaan alokasi objek. Pemicunya beragam — koleksi yang tumbuh tanpa batas, memuat berkas raksasa ke memori — dan pemulihannya hampir selalu di luar kendali kode yang sedang berjalan.
Kedua nama itu berakhiran Error, bukan Exception, dan penamaan tersebut konsisten di seluruh Java: NoClassDefFoundError, AssertionError, ExceptionInInitializerError — semuanya menyampaikan pesan yang sama, yaitu “sistem atau program berada di keadaan yang tidak lagi sehat untuk dilanjutkan”.
Demo StackOverflowError dari Rekursi Tanpa Kondisi Dasar #
Mari kita lihat StackOverflowError lahir secara nyata. Method berikut memanggil dirinya sendiri tanpa pernah berhenti — tidak ada kondisi dasar yang mengakhiri rekursi:
public class RekursiTanpaBasis {
static int hitungMundur(int n) {
// tanpa kondisi dasar: rekursi berjalan terus tanpa berhenti
return hitungMundur(n + 1);
}
public static void main(String[] args) {
System.out.println("memulai hitung mundur");
hitungMundur(1);
System.out.println("selesai");
}
}Baris memulai hitung mundur tercetak, lalu program berhenti dengan exit code 1. Setiap pemanggilan hitungMundur menambah satu frame ke stack; setelah belasan ribu frame (jumlah pastinya bergantung ukuran stack, yang berbeda-beda antar lingkungan), JVM tidak punya ruang lagi dan melempar StackOverflowError. Default handler mencetak baris pembuka Exception in thread "main" java.lang.StackOverflowError, diikuti stack trace yang berisi frame hitungMundur berulang puluhan ribu kali pada baris yang sama — potret rekursi yang berputar tanpa henti. Perhatikan detail menariknya: meski bertitel Error, format pelaporannya sama persis dengan exception — keduanya adalah Throwable.
Secara teknis, Error bisa ditangkap — kompilator menerimanya karena penangkapan Throwable memang sah. Program eksperimen berikut menangkap StackOverflowError hanya untuk membuktikan hal itu:
public class TangkapStackOverflow {
static int hitungMundur(int n) {
return hitungMundur(n + 1);
}
public static void main(String[] args) {
try {
hitungMundur(1);
} catch (StackOverflowError e) {
System.out.println("tertangkap: " + e.getClass().getSimpleName());
System.out.println("pesan : " + e.getMessage());
}
System.out.println("program tetap berjalan");
}
}Output yang Dihasilkan:
tertangkap: StackOverflowError
pesan : null
program tetap berjalanMari kita bedah hasilnya:
- Baris
tertangkap: StackOverflowError: tangkapan berhasil — stack unwinding mengeluarkan seluruh frame rekursi, sehinggamainmelanjutkan eksekusi dengan stack yang bersih. - Baris
pesan : null:StackOverflowErrorbiasanya dibuat tanpa pesan — tidak ada satu teks yang bermakna untuk “stack penuh”. MethodgetMessage()mengembalikannull. - Baris
program tetap berjalan: program bertahan. Namun bertahannya program bukan berarti masalahnya selesai — bug rekursi masih ada dan akan muncul kembali sebagai kegagalan yang sama pada pemanggilan berikutnya.
Error dan Exception Berdampingan #
Perbedaan kedua cabang tersebut kini bisa dirangkum utuh:
| Aspek | Error | Exception |
|---|---|---|
| Makna | kegagalan fatal JVM/sistem | kegagalan di dalam logika program |
| Contoh | StackOverflowError, OutOfMemoryError | IOException, NumberFormatException |
Ditangkap catch (Exception e)? | tidak | ya |
| Pemulihan wajar | perbaiki penyebab atau restart proses | tangani, ulangi, atau laporkan |
Klaim throws di tanda tangan | tidak diwajibkan (berperilaku unchecked) | checked wajib dinyatakan |
Untuk OutOfMemoryError, kita tidak menyiapkan demo runnable yang bisa dipercaya lintas mesin: memaksa heap habis butuh alokasi bergantung pada ukuran memori lingkungan eksekusi. Konsepnya saja yang perlu melekat — heap penuh berarti JVM gagal memenuhi new, dan satu-satunya pemulihan yang bisa diandalkan adalah di luar kode aplikasi.
Satu topik tersisa di Section ini, dan topik tersebut merangkum semuanya: ketika exception benar-benar melanda di kode produksi, kapan kita menangani, kapan membungkus, dan kapan meneruskan? Chapter penutup menyusun strategi penanganan exception menjadi keputusan yang sadar — beserta daftar kebiasaan yang sebaiknya tidak pernah kita tiru.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
RekursiTanpaBasis.java
Unduh berkaspublic class RekursiTanpaBasis {
static int hitungMundur(int n) {
// tanpa kondisi dasar: rekursi berjalan terus tanpa berhenti
return hitungMundur(n + 1);
}
public static void main(String[] args) {
System.out.println("memulai hitung mundur");
hitungMundur(1);
System.out.println("selesai");
}
}
TangkapStackOverflow.java
Unduh berkaspublic class TangkapStackOverflow {
static int hitungMundur(int n) {
return hitungMundur(n + 1);
}
public static void main(String[] args) {
try {
hitungMundur(1);
} catch (StackOverflowError e) {
System.out.println("tertangkap: " + e.getClass().getSimpleName());
System.out.println("pesan : " + e.getMessage());
}
System.out.println("program tetap berjalan");
}
}