Semua exception di Java adalah objek, dan seperti objek lain, semuanya punya tempat di pohon pewarisan. Satu akar bernama Throwable menurunkan dua cabang besar — Error dan Exception — yang kemudian bercabang lagi menjadi RuntimeException dan ratusan jenis turunan lain. Memahami peta ini bukan hafalan kosong: tipe exception menentukan blok catch mana yang cocok, menentukan apakah compiler memaksa kita menanganinya, dan menentukan seberapa spesifik pesan kegagalan yang bisa kita sampaikan. Pada Chapter ini kita memetakan hierarki tersebut lalu berkenalan dengan anggota-anggotanya yang paling sering muncul.

Pohon Keluarga Throwable #

Class Throwable di package java.lang adalah akar seluruh kegagalan yang bisa dilempar di Java. Throwable memiliki dua cabang langsung: Error untuk kondisi fatal internal JVM, dan Exception untuk kegagalan yang relevan bagi program kita. Cabang Exception terbagi lagi: RuntimeException beserta turunannya, dan sisa exception lain seperti IOException atau SQLException. Peta lengkapnya tampak seperti ini:

Karena pewarisan berlaku penuh, setiap turunan adalah bentuk dari semua parent-nya. Operator instanceof memungkinkan kita memverifikasi hubungan tersebut secara nyata:

public class UjiInstanceOf {
    static void periksa(Throwable kegagalan) {
        System.out.println(kegagalan.getClass().getSimpleName()
                + " -> RuntimeException: " + (kegagalan instanceof RuntimeException)
                + " | Exception: " + (kegagalan instanceof Exception)
                + " | Error: " + (kegagalan instanceof Error)
                + " | Throwable: " + (kegagalan instanceof Throwable));
    }

    public static void main(String[] args) {
        periksa(new NumberFormatException("bukan angka"));
        periksa(new java.io.IOException("berkas tidak terbaca"));
        periksa(new InterruptedException("thread diganggu"));
        periksa(new StackOverflowError());
    }
}

Output yang Dihasilkan:

text
NumberFormatException -> RuntimeException: true | Exception: true | Error: false | Throwable: true
IOException -> RuntimeException: false | Exception: true | Error: false | Throwable: true
InterruptedException -> RuntimeException: false | Exception: true | Error: false | Throwable: true
StackOverflowError -> RuntimeException: false | Exception: false | Error: true | Throwable: true

Mari kita baca hasilnya:

  1. Baris pertama (NumberFormatException ...): karena NumberFormatException adalah turunan RuntimeException, objek tersebut sekaligus merupakan RuntimeException, Exception, dan Throwable — tetapi bukan Error.
  2. Baris kedua (IOException ...): IOException turun langsung dari Exception, bukan dari RuntimeException, sehingga kolom pertama bernilai false.
  3. Baris ketiga (InterruptedException ...): sama seperti IOException, exception ini berada di luar cabang RuntimeException.
  4. Baris keempat (StackOverflowError ...): Error dan Exception adalah cabang yang berbeda — sebuah Error bukan Exception, meski keduanya Throwable.

Exception yang Sering Kita Temui #

Pustaka standar menyediakan ratusan jenis exception, tetapi sebagian kecil muncul jauh lebih sering. Tabel berikut merangkum yang paling penting:

ExceptionPackagePemicu umum
NullPointerExceptionjava.langmengakses anggota objek yang masih null
ArrayIndexOutOfBoundsExceptionjava.langmengakses indeks di luar panjang array
ArithmeticExceptionjava.langoperasi aritmetika ilegal, misalnya bilangan bulat dibagi nol
NumberFormatExceptionjava.langkonversi teks ke angka gagal, misalnya Integer.parseInt("abc")
ClassCastExceptionjava.langpenurunan tipe (downcast) ke tipe yang tidak sesuai
IOExceptionjava.iokegagalan baca/tulis berkas, jaringan, atau stream
SQLExceptionjava.sqlkegagalan operasi basis data lewat JDBC
InterruptedExceptionjava.langthread diganggu saat menunggu atau tidur

Empat exception pertama adalah turunan RuntimeException — semuanya bisa kita picu dan amati dalam satu program:

public class ExceptionUmum {
    public static void main(String[] args) {
        try {
            System.out.println(Integer.parseInt("abc"));
        } catch (NumberFormatException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }

        try {
            System.out.println(10 / 0);
        } catch (ArithmeticException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }

        try {
            int[] nilai = {70, 85, 90};
            System.out.println(nilai[5]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }

        try {
            String nama = null;
            System.out.println(nama.length());
        } catch (NullPointerException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }
    }
}

Output yang Dihasilkan:

text
NumberFormatException -> For input string: "abc"
ArithmeticException -> / by zero
ArrayIndexOutOfBoundsException -> Index 5 out of bounds for length 3
NullPointerException -> Cannot invoke "String.length()" because "<local1>" is null

Setiap baris keluaran menampilkan pola NamaException -> pesan. Perhatikan bahwa pesan bawaan pustaka sudah cukup bercerita: For input string: "abc" menyebut teks yang gagal dikonversi, Index 5 out of bounds for length 3 menyebut indeks yang diminta dan panjang sebenarnya, dan / by zero langsung menunjuk operasi pembagian. Pada NullPointerException, JVM modern bahkan menjelaskan operasi yang gagal (Cannot invoke "String.length()") dan menunjuk variabel lokal penyebabnya lewat penanda internal seperti <local1> — penanda yang mencatat posisi variabel di method, bukan nama variabelnya.

Kenapa Hierarki Penting #

Struktur pohon ini bekerja untuk kita di tiga tempat. Pertama, saat menangkap exception, sebuah catch menerima exception bertipe yang ditulis atau semua turunannya — catch (Exception e) menangkap hampir semuanya, sedangkan catch (NumberFormatException e) hanya menerima jenis itu. Kedua, saat melempar exception dari method, kita bisa memilih tipe yang paling menggambarkan masalah: pembagian nol memakai ArithmeticException, bukan Exception yang serba umum. Ketiga, compiler memakai posisi dalam hierarki untuk menentukan apakah sebuah exception wajib ditangani — pemisahan antara cabang RuntimeException dan cabang lainnya menjadi tema Chapter Checked vs Unchecked Exceptions.

Sebelum sampai ke sana, kita perlu keterampilan paling dasar: menangkap exception yang sedang terjadi. Chapter berikutnya membahas blok try-catch — bentuk, alur lompatannya, dan cara membaca objek exception yang tertangkap.

Referensi #

Berkas Sumber

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

ExceptionUmum.java

Unduh berkas
java
public class ExceptionUmum {
    public static void main(String[] args) {
        try {
            System.out.println(Integer.parseInt("abc"));
        } catch (NumberFormatException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }

        try {
            System.out.println(10 / 0);
        } catch (ArithmeticException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }

        try {
            int[] nilai = {70, 85, 90};
            System.out.println(nilai[5]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }

        try {
            String nama = null;
            System.out.println(nama.length());
        } catch (NullPointerException e) {
            System.out.println(e.getClass().getSimpleName() + " -> " + e.getMessage());
        }
    }
}

UjiInstanceOf.java

Unduh berkas
java
public class UjiInstanceOf {
    static void periksa(Throwable kegagalan) {
        System.out.println(kegagalan.getClass().getSimpleName()
                + " -> RuntimeException: " + (kegagalan instanceof RuntimeException)
                + " | Exception: " + (kegagalan instanceof Exception)
                + " | Error: " + (kegagalan instanceof Error)
                + " | Throwable: " + (kegagalan instanceof Throwable));
    }

    public static void main(String[] args) {
        periksa(new NumberFormatException("bukan angka"));
        periksa(new java.io.IOException("berkas tidak terbaca"));
        periksa(new InterruptedException("thread diganggu"));
        periksa(new StackOverflowError());
    }
}