Satu blok try sering menghadirkan lebih dari satu jenis kegagalan: masukan yang tidak bisa dikonversi, pembagian dengan nol, atau data yang belum terisi. Menangkap semuanya dengan satu catch (Exception e) membuat kita kehilangan kejelasan — tiap jenis kegagalan idealnya mendapat tanggapan yang tepat. Java menyediakan dua mekanisme untuk itu: beberapa blok catch berurutan untuk tanggapan yang berbeda-beda, dan sintaks multi-catch untuk menggabungkan beberapa tipe dalam satu blok tanggapan yang sama. Pada Chapter ini kita pelajari keduanya beserta aturan urutan yang ditegakkan compiler.
Blok catch Berurutan #
Blok catch boleh ditulis lebih dari satu setelah blok try, dan JVM memeriksa kecocokannya secara berurutan dari atas ke bawah. Blok pertama yang tipenya cocok dengan exception yang sedang terbang akan dijalankan; blok-blok setelahnya diabaikan untuk exception tersebut. Program berikut memproses beberapa masukan dengan tiga blok catch yang masing-masing menangani jenis kegagalan berbeda:
public class CatchBerurutan {
static void proses(String teks) {
System.out.println("memproses \"" + teks + "\"");
try {
int nilai = Integer.parseInt(teks.trim());
System.out.println(" hasil: " + (100 / nilai));
} catch (NumberFormatException e) {
System.out.println(" teks bukan angka: " + e.getMessage());
} catch (ArithmeticException e) {
System.out.println(" pembagian nol: " + e.getMessage());
} catch (Exception e) {
System.out.println(" kegagalan lain: " + e.getClass().getSimpleName());
}
}
public static void main(String[] args) {
proses("25");
proses(" abc ");
proses("0");
proses(null);
}
}Output yang Dihasilkan:
memproses "25"
hasil: 4
memproses " abc "
teks bukan angka: For input string: "abc"
memproses "0"
pembagian nol: / by zero
memproses "null"
kegagalan lain: NullPointerExceptionMari kita bedah pemetaan kegagalan ke blok tanggapannya:
- Pemanggilan
proses("25"): kedua statement di bloktryberhasil — tidak adacatchyang berjalan, dan hasil4tercetak dari dalamtry. - Pemanggilan
proses(" abc "):teks.trim()menghasilkan"abc", laluInteger.parseIntmelemparNumberFormatException— blok pertama yang cocok. Pencetakan100 / nilaiterlewati. - Pemanggilan
proses("0"): parsing berhasil, tetapi100 / 0melemparArithmeticException— blok kedua yang berjalan. - Pemanggilan
proses(null):teks.trim()langsung melemparNullPointerException. Dua blok pertama tidak cocok tipenya, sehingga blok terakhircatch (Exception e)— penangkap umum — yang menangkap. Perhatikan keluaranmemproses "null": penggabungan string dengan referensinullmencetak teksnull, sehingga baris itu tetap tercetak sebelum kegagalan terjadi.
Urutan NumberFormatException → ArithmeticException → Exception pada contoh di atas bukan kebetulan. Karena pencocokan dilakukan dari atas, tipe yang lebih spesifik ditempatkan lebih dulu, dan tipe umum ditempatkan paling akhir sebagai jaring pengaman. Kalau kita membalik urutannya, seluruh exception akan tertangkap blok Exception lebih dulu dan blok-blok spesifik tidak pernah terpakai.
Urutan yang Salah Ditolak Compiler #
Masalahnya, compiler tidak membiarkan kita membuat blok spesifik yang mustahil terjangkau. Jika sebuah catch bertipe parent (misalnya Exception) diletakkan sebelum catch bertipe child-nya (misalnya NumberFormatException), compiler menolak program:
public class UrutanSalah {
public static void main(String[] args) {
try {
int nilai = Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("tangkap umum");
} catch (NumberFormatException e) {
System.out.println("tangkap khusus");
}
}
}Kompilasi berakhir dengan pesan error: exception NumberFormatException has already been caught yang menunjuk baris catch (NumberFormatException e). Alasannya lugas: NumberFormatException adalah turunan Exception, dan blok catch (Exception e) di atasnya sudah menangkap semua exception bertipe Exception beserta turunannya — blok kedua tidak akan pernah bisa dijalankan, sehingga compiler menolak kode yang tidak berarti itu.
Multi-catch dengan Tanda Pipa #
Sering kali dua jenis kegagalan justru butuh tanggapan yang sama persis. Daripada menduplikasi isi blok, Java sejak versi 7 menyediakan sintaks multi-catch: beberapa tipe ditulis dalam satu klausa catch dan dipisah tanda pipa (|). Contoh klasiknya adalah membaca dan mengonversi isi stream — kegagalan baca (IOException) dan kegagalan konversi (NumberFormatException) sama-sama cukup dilaporkan sebagai baris yang tidak bisa diproses:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
public class MultiCatch {
public static void main(String[] args) throws IOException {
BufferedReader pembaca = new BufferedReader(new StringReader("12\nabc"));
for (int i = 1; i <= 2; i++) {
try {
String baris = pembaca.readLine();
int nilai = Integer.parseInt(baris);
System.out.println("baris " + i + ": " + nilai);
} catch (IOException | NumberFormatException e) {
System.out.println("baris " + i + " gagal: " + e.getClass().getSimpleName());
}
}
pembaca.close();
}
}Output yang Dihasilkan:
baris 1: 12
baris 2 gagal: NumberFormatExceptionMari kita amati detail pentingnya:
- Baris 6 (
main(String[] args) throws IOException):pembaca.close()di akhir bisa melemparIOExceptionyang berada di luar bloktry, sehinggamainharus menyatakan kemungkinan itu lewat klausathrows— detail lengkapnya dibahas pada Chapter throw dan throws. - Baris 12 (
catch (IOException | NumberFormatException e)): satu variabeleuntuk dua tipe. Baris pertama berhasil dikonversi menjadi12; baris kedua mengandung"abc"sehinggaNumberFormatExceptiontertangkap di sini. - Baris 13 (
e.getClass().getSimpleName()): pada multi-catch, tipe statiseadalah parent terdekat bersama dari kedua tipe — di siniException— sehinggagetClass().getSimpleName()menjadi cara tepat untuk mengetahui jenis kegagalan yang sebenarnya saat program berjalan.
Kapan memilih yang mana? Jika dua kegagalan ditangani dengan kode yang identik, gabungkan dengan | agar tidak ada duplikasi. Jika tanggapannya berbeda — pesan berbeda, pemulihan berbeda — pisahkan menjadi blok catch berurutan dari yang paling spesifik ke yang paling umum.
Setelah blok catch selesai, satu pertanyaan kerap muncul: bagaimana menjamin pembersihan — menutup berkas, memutus koneksi — tetap berjalan baik saat try sukses maupun saat exception terbang? Jawabannya adalah blok finally, topik Chapter berikutnya.
Referensi #
- Oracle: Catching Multiple Exception Types di dev.java
- Class IOException pada API java.io
- Class NumberFormatException pada API java.lang
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
CatchBerurutan.java
Unduh berkaspublic class CatchBerurutan {
static void proses(String teks) {
System.out.println("memproses \"" + teks + "\"");
try {
int nilai = Integer.parseInt(teks.trim());
System.out.println(" hasil: " + (100 / nilai));
} catch (NumberFormatException e) {
System.out.println(" teks bukan angka: " + e.getMessage());
} catch (ArithmeticException e) {
System.out.println(" pembagian nol: " + e.getMessage());
} catch (Exception e) {
System.out.println(" kegagalan lain: " + e.getClass().getSimpleName());
}
}
public static void main(String[] args) {
proses("25");
proses(" abc ");
proses("0");
proses(null);
}
}
MultiCatch.java
Unduh berkasimport java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
public class MultiCatch {
public static void main(String[] args) throws IOException {
BufferedReader pembaca = new BufferedReader(new StringReader("12\nabc"));
for (int i = 1; i <= 2; i++) {
try {
String baris = pembaca.readLine();
int nilai = Integer.parseInt(baris);
System.out.println("baris " + i + ": " + nilai);
} catch (IOException | NumberFormatException e) {
System.out.println("baris " + i + " gagal: " + e.getClass().getSimpleName());
}
}
pembaca.close();
}
}
UrutanSalah.java
Unduh berkaspublic class UrutanSalah {
public static void main(String[] args) {
try {
int nilai = Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("tangkap umum");
} catch (NumberFormatException e) {
System.out.println("tangkap khusus");
}
}
}