Membuat
program untuk bekerja dalam kondisi ideal jauh lebih mudah daripada
membuat program yang tangguh. Program tangguh dapat tahan dari kondisi
yang tidak biasa atau dalam kondisi "pengecualian". Salah satu
pendekatanya adalah dengan melihat kemungkinan masalah yang mungkin
terjadi dan menambahkan tes/pengujian pada program tersebut untuk setiap
kemungkinan masalah yang mungkin terjadi.
Misalnya, program akan
berhenti jika mencoba mengakses elemen array A[i], di mana i tidak
berada di dalam rentang yang dibolehkan. Program tangguh harus dapat
mengantisipasi kemungkinan adanya indeks yang tak masuk akal dan
menjaganya dari kemungkinan itu. Misalnya bisa dilakukan dengan :
if (i < 0 || i >= A.length) { ... // Lakukan sesuatu untuk menangani indeks i diluar rentang } else { ... // Proses elemen A[i] }
if
.Java (seperti C++) membutuhkan metode alternatif yang lebih rapi dan terstruktur untuk menghadapi masalah yang mungkin terjadi ketika program dijalankan. Metode ini disebut sebagai penanganan pengecualian (exception-handling). Kata "pengecualian" diartikan sesuatu yang lebih umum daripada "kesalahan". Termasuk di antaranya adalah kondisi yang mungkin terjadi yang berada di luar aliran suatu program. Pengecualian, bisa berupa kesalahan, atau bisa juga kasus tertentu yang kita inginkan terpisah dari algoritma kita.
Ketika pengecualian terjadi dalam eksekusi suatu program, kita sebut bahwa pengecualian tersebut di-lempar-kan (thrown). Ketika ini terhadi, aliran program artinya terlempar dari jalurnya, dan program berada dalam bahaya akan crash. Akan tetapi crash bisa dihindari jika pengecualian tersebut ditangkap (catch) dan ditangani dengan cara tertentu. Suatu pengecualian dapat dilempar oleh satu bagian program dan ditangkap oleh bagian program lain. Pengecualian yang tidak ditangkap secara umum akan menyebabkan program berhenti. (Lebih tepat apabila disebut thread yang melemparkan pengecualian tersebut akan berhenti. Dalam program multithreading, mungkin saja thread lain akan terus berjalan apabila thread yang satu berhenti.)
Karena program Java dijalankan di dalam interpreter, program yang crash berarti bahwa program tersebut berhenti berjalan secara prematur. Tidak berarti bahwa interpreter juga akan crash. Atau dengan kata lain, interpreter akan menangkap pengecualian yang tidak ditangkap oleh program. Interpreter akan merespon dengan menghentikan jalannya program. Dalam banyak bahasa pemrograman lainnya, program yang crash sering menghentikan seluruh sistem hingga kita menekan tombol reset. Dalam Java, kejadian seperti itu tidak mungkin -- yang artinya ketika hal itu terjadi, maka yang salah adalah komputer kita, bukan program kita.
Ketika pengecualian terjadi, yang terjadi adalah program tersebut melemparkan suatu objek. Objek tersebut membawa informasi (dalam variabel instansinya) dari tempat di mana pengecualian terjadi ke titik di mana ia bisa ditangkap dan ditangani. Informasi ini selalu terdiri dari tumpukan panggilan subrutin (subrutin call stack), yaitu daftar di mana dan dari mana subrutin tersebut dipanggil dan kapan pengecualian tersebut dilemparkan. (Karena suatu subrutin bisa memanggil subrutin yang lain, beberapa subrutin bisa aktif dalam waktu yang sama.) Biasanya, objek pengecualian juga memiliki pesan kesalahan mengapa ia dilemparkan, atau bisa juga memiliki data lain. Objek yang dilemparkan harus diciptakan dari kelas standar
java.lang.Throwable
atau kelas turunannya.Secara umum, setiap jenis pengecualian dikelompokkan dalam kelas turunan
Throwable
. Throwable
memiliki dua kelas turunan langsung, yaitu Error
dan Exception
.
Kedua kelas turunan ini pada akhirnya memiliki banyak kelas turunan
lain. Kita juga bisa membuat kelas pengecualian baru untuk melambangkan
jenis pengecualian baru.Kebanyakan turunan dari kelas
Error
merupakan kesalahan serius dalam mesin virtual Java yang memang
seharusnya menyebabkan berhentinya program karena sudah tidak dapat
diselamatkan lagi. Kita sebaiknya tidak mencoba untuk menangkap dan
menangani kesalahan jenis ini. Misalnya ClassFormatError
dilempar karena mesin virtual Java menemukan data ilegal dalam suatu
file yang seharusnya berisi kelas Java yang sudah terkompilasi. Jika
kelas tersebut dipanggil ketika program sedang berjalan, maka kita tidak
bisa melanjutkan program tersebut sama sekali.Di lain pihak, kelas turunan dari kelas
Exception
melambangkan pengecualian yang memang seharusnya ditangkap. Dalam
banyak kasus, pengecualian seperti ini adalah sesuatu yang mungkin biasa
disebut "kesalahan", akan tetapi kesalahan seperti ini adalah jenis
yang bisa ditangani dengan cara yang baik. (Akan tetapi, jangan terlalu
bernafsu untuk menangkap semua kesalahan hanya karena kita ingin program
kita tidak crash. Jika kita tidak memiliki cara untuk menanganinya,
mungkin menangkap pengecualian dan membiarkannya akan menyebabkan
masalah lain di tempat lain).Kelas
Exception
memiliki kelas turunan lainnnya, misalnya RuntimeException
. Kelas ini mengelompokkkan pengecualian umum misalnya ArithmeticException
yang terjadi misalnya ada pembagian dengan nol, ArrayIndexOutOfBoundsException
yang terjadi jika kita mencoba mengakses indeks array di luar rentang yang diijinkan, dan NullPointerException
yang terjadi jika kita mencoba menggunakan referensi ke null
di mana seharusnya referensi objek diperlukan.RuntimeException
biasanya menginidikasikan adanya bug dalam program yang harus diperbaiki oleh programmer. RuntimeException
dan Error
memiliki sifat yang sama yaitu program bisa mengabaikannya.
("Mengabaikan" artinya kita membiarkan program crash jika pengecualian
tersebut terjadi). Misalnya, program yang melemparkan ArrayIndexOutOfBoundsException
dan tidak menanganinya akan menghentikan program saat itu juga. Untuk pengecualian lain selain Error
dan RuntimeException
beserta kelas turunannya, pengecualian wajib ditangani.Diagram berikut menggambarkan hirarki suatu kelas turunan dari kelas
Throwable
. Kelas yang membutuhkan penanganan pengecualian wajib ditunjukkan dalam kotak merah.Untuk menangkap pengecualian pada program Java, kita menggunakan pernyataan
try
.
Maksudnya memberi tahu komputer untuk "mencoba" (try) menjalankan suatu
perintah. Jika berhasil, semuanya akan berjalan seperti biasa. Tapi
jika pengecualian dilempar pada saat mencoba melaksanakan perintah
tersebut, kita bisa menangkapnya dan menanganinya. Misalnya,try { double determinan = M[0][0]*M[1][1] - M[0][1]*M[1][0]; System.out.println("Determinan matriks M adalah " + determinan); } catch ( ArrayIndexOutOfBoundsException e ) { System.out.println("Determinan M tidak bisa dihitung karena ukuran M salah."); }
try
". Jika tidak ada pengecualian, maka bagian "catch
" akan diabaikan. Akan tetapi jika ada pengecualian ArrayIndexOutOfBoundsException
, maka komputer akan langsung lompat ke dalam blok setelah pernyataan "catch (ArrayIndexOutOfBoundsException)
". Blok ini disebut blok yang menangani pengecualian (exception handler) untuk pengecualian ArrayIndexOutOfBoundsException
". Dengan cara ini, kita mencegah program berhenti tiba-tiba.Mungkin kita akan sadar bahwa ada kemungkinan kesalahan lagi dalam blok di dalam pernyataan
try
. Jika variabel M berisi null
, maka pengecualian NullPointerException
akan dilemparkan. Dalam pernyataan try
di atas, pengecualian NullPointerException
tidak ditangkap, sehingga akan diproses seperti biasa (yaitu
menghentikan program saat itu juga, kecuali pengecualian ini ditangkap
di tempat lain). Kita bisa menangkap pengecualian NullPointerException
dengan menambah klausa catch
lain, seperti :try { double determinan = M[0][0]*M[1][1] - M[0][1]*M[1][0]; System.out.println("Determinan matriks M adalah " + determinan); } catch ( ArrayIndexOutOfBoundsException e ) { System.out.println("Determinan M tidak bisa dihitung karena ukuran M salah."); } catch ( NullPointerException e ) { System.out.print("Kesalahan program! M tidak ada: " + ); System.out.println( e.getMessage() ); }
catch
. e
adalah nama variabel (bisa kita ganti dengan nama apapun terserah
kita). Ingat kembali bahwa ketika pengecualian terjadi, sebenarnya yang
dilemparkan adalah objek. Sebelum menjalankan klausa catch
, komputer mengisi variabel ini dengan objek yang akan ditangkap. Objek ini mengandung informasi tentang pengecualian tersebut.Misalnya, pesan kesalahan yang menjelaskan tentang pengecualian ini bisa diambil dengan metode
getMessage()
seperti contoh di atas. Metode ini akan mencetak daftar subrutin yang
dipanggil sebelum pengecualian ini dilempar. Informasi ini bisa menolong
kita untuk melacak dari mana kesalahan terjadi.Ingat bahwa baik
ArrayIndexOutOfBoundsException
dan NullPointerException
adalah kelas turunan dari RuntimeException
. Kita bisa menangkap semua pengecualian dalam kelas RuntimeException
dengan klausa catch
tunggal, misalnya :try { double determinan = M[0][0]*M[1][1] - M[0][1]*M[1][0]; System.out.println("Determinan matriks M adalah " + determinan); } catch ( RuntimeException e ) { System.out.println("Maaf, ada kesalahan yang terjadi."); e.printStackTrace(); }
ArrayIndexOutOfBoundsException
maupun NullPointerException
juga bertipe RuntimeException
,
maka perintah seperti di atas akan menangkap kesalahan indeks maupun
kesalahan pointer kosong dan juga pengecualian runtime yang lain. Ini
menunjukkan mengapa kelas pengecualian disusun dalam bentuk hirarki
kelas. Kita bisa membuat klausa catch
secara spesifik hingga tahu persis apa yang salah, atau menggunakan klausa penangkap umum.Contoh di atas mungkin tidak begitu realistis. Sepertinya kita jarang harus menangani kesalahan indeks ataupun kesalahan pointer kosong seperti di atas. Masalahnya mungkin terlalu banyak, dan mungkin kita akan bosan jika harus menulis
try ... catch
setiap kali kita menggunakan array. Yang penting kita mengisi variabel tersebut dengan sesuatu yang bukan null
dan menjaga agar program tidak keluar indeks sudah cukup. Oleh karena
itu kita sebut penanganan ini tidak wajib. Akan ada banyak hal yang bisa
jadi masalah. (Makanya penanganan pengecualian tidak menyebabkan
program makin tangguh. Ia hanya memberikan alat yang mungkin kita
gunakan dengan cara memberi tahu di mana kesalahan mungkin muncul).Bentuk pernyataan
try
sebenarnya lebih kompleks dari contoh di atas. Sintaksnya secara umum dapat ditulis seperti:try { perintah } klausa_catch_tambahan klausa_finally_tambahan
try
boleh tidak dimasukkan, dan juga klausa finally
boleh ada boleh tidak. (Pernyataan try
harus memiliki satu klausa finally
atau catch
). Sintaks dari klausa catch
adalah:catch (nama_kelas_pengecualian nama_variabel) { pernyataan }
finally
adalahfinally { pernyataan }
finally
yaitu pernyataan di dalam klausa finally
akan dijamin untuk dilaksanakan sebagai perintah akhir dari pernyataan try
, tidak peduli apakah ada pengecualian yang dilempar atau tidak. Pada dasarnya klausa finally
dimaksudkan untuk melakukan langkah pembersihan yang tidak boleh dihilangkan.Ada beberapa kejadian di mana suatu program memang harus melempar pengecualian. Misalnya apabila program tersebut menemukan adanya kesalahan pengurutan atau kesalahan lain, tapi tidak ada cara yang tepat untuk menanganinya di tempat di mana kesalahan tersebut ditemukan. Program bisa melempar pengecualian dengan harapan di bagian lain pengecualian ini akan ditangkap dan diproses.
Untuk melempar pengecualian, kita bisa menggunakan pernyataan
throw
dengan sintaks :throw objek_pengecualian;
objek_pengecualian
harus merupakan objek yang bertipe kelas yang diturunkan dari Throwable
. Biasanya merupakan kelas turunan dari kelas Exception
. Dalam banyak kasus, biasanya objek tersebut dibuat dengan operator new
, misalnya :throw new ArithmeticException("Pembagian dengan nol");
Pengecualian bisa dilempar baik oleh sistem (karena terjadinya kesalahan) atau oleh pernyataan
throw
. Pengecualian ini ditangani dengan cara yang sama. Misalnya suatu pengecualian dilempar dari pernyataan try
. Jika pernyataan try
tersebut memiliki klausa catch
untuk menangani tipe pengecualian tersebut, maka klausa ini akan melompat ke klausa catch
dan menjalankan perintah di dalamnya. Pengecualian tersebut telah ditangani.Setelah menangani pengecualian tersebut, komputer akan menjalankan klausa
finally
,
jika ada. Kemudian program akan melanjutkan program seperti biasa. Jika
suatu pengecualian tidak ditangkap dan ditangani, maka pengolahan
pengecualian akan berlanjut.Jika pengecualian dilempar pada saat eksekusi suatu subrutin dan pengecualian tersebut tidak ditangani di dalam subrutin yang sama, maka subrutin tersebut akan dihentikan (setelah menjalankan klausa
finally
,
jika tersedia). Kemudian rutin yang memanggil subrutin tersebut
memiliki kesempatan untuk menangani pengecualian tersebut. Artinya, jika
subrutin tersebut dipanggil di dalam pernyataan try
dan memiliki klausa catch
yang cocok, maka klausa catch
tersebut akan dieksekusi dan program akan berlanjut seperti biasa.Lagi-lagi jika rutin tersebut tidak menangani pengecualian tersebut, rutin tersebut akan dihentikan dan rutin yang memanggilnya akan mencoba menangani pengecualian tersebut. Pengecualian akan menghentikan program secara keseluruhan jika keseluruhan rantai panggil subrutin hingga
main()
tidak menangani pengecualian tersebut.Suatu subrutin yang mungkin melempar pengecualian dapat memberi tahu dengan menambahkan klausa "
throws nama_kelas_pengecualian
" pada definisi suatu subrutin. Misalnya :static double akar(double A, double B, double C) throws IllegalArgumentException { // Menghasilkan akar yang lebih besar // dari persamaan kuadrat A*x*x + B*x + C = 0. // (Lempar pengecualian jika A == 0 atau B*B-4*A*C < 0.) if (A == 0) { throw new IllegalArgumentException("A tidak boleh nol."); } else { double diskriminan = B*B - 4*A*C; if (diskriminan < 0) throw new IllegalArgumentException("Diskriminan < nol."); return (-B + Math.sqrt(diskriminan)) / (2*A); } }
A != 0
dan B*B-4*A*C >= 0
. Subrutin akan melempar pengecualian dengan tipe IllegalArgumentException
jika salah satu dari kondisi awal ini tidak dipenuhi. Jika kondisi
ilegal ditemukan dalam suatu subrutin, melempar pengecualian kadang kala
menjadi pilihan yang lebih bijaksana. Jika program yang memanggil
subrutin tersebut mengetahui bagaimana cara yang tepat untuk menangani
pengecualian tersebut, program tersebut dapat menangkapnya. JIka tidak,
maka program akan crash dan programmer akan tahu apa yang harus
diperbaiki.Penanganan Pengecualian Wajib
Dalam contoh-contoh sebelumnya, mendeklarasikan subrutin
akar()
yang dapat melempar pengecualian IllegalArgumentException
adalah sesuatu yang "sopan" untuk pembaca rutin tersebut. Menangani IllegalArgumentException
bukan sesuatu yang "wajib" dilakukan. Subruin dapat melempar IllegalArgumentException
tanpa mengumumkan kemungkinan tersebut. Kemudian program yang memanggil
rutin tersebut bebas untuk menangkap atau membiarkan pengecualian yang
dilempar oleh rutin tersebut. Begitu juga halnya dengan tipe NullPointerException[/code, programmer bebas untuk menangkap atau mengabaikan pengecualian tersebut.</p><p>Untuk kelas pengecualian yang mewajibkan penanganan pengecualian, siatuasinya sedikit berbeda. Jika suatu subrutin bisa melempar pengecualian seperti ini, maka klausa [code]throws
harus ditulis pada definisi subrutin. Jika tidak, maka compiler akan menampilkan kesalahan sintaks.Di sisi lain, misalnya suatu pernyataan dalam program dapat melemparkan pengecualian yang mewajibkan penanganan pengecualian. Pernyataan tersebut bisa berasal dari pernyataan
throw
yang sengaja dilemparkan atau hasil dari pemanggilan suatu subrutin
yang melempar pengecualian tersebut. Pengecualian ini harus ditangani
dengan salah satu dari dua cara, yaitu : Menggunakan pernyataan try
yang memiliki klausa catch
untuk menangani pengecualian tersebut. Atau dengan menambahkan klausa throws
di kepala definisi subrutin. Jika klausa
throws
digunakan, maka subrutin lain yang memanggil subrutin kita akan
bertanggung jawab menangani pengecualian tersebut. Jika kita tidak
menangani pengecualian tersebut dengan cara-cara di atas, maka Java akan
menganggap sebagai kesalahan sintaks.Penanganan pengecualian menjadi wajib untuk kelas yang bukan kelas turunan dari
Error
atau RuntimeException
.
Pengecualian yang wajib biasanya merupakan kondisi yang berada di luar
jangkauan programmer. Misalnya, input ilegal atau aksi ilegal oleh user.
Program tangguh harus bisa menangani kondisi seperti ini. Desain Java
tidak memungkinkan programmer untuk mengabaikan kondisi tersebut.Di antara pengecualian yang mewajibkan penanganan pengecualian adalah yang berhubungan dengan rutin input/output. Artinya kita tidak boleh menggunakan rutin ini jika kita mengetahui bagaimana menangani pengecualian. Bagian berikutnya akan membahas tentang contoh-contoh input/output dan penanganan pengecualian dengan lebih gamblang.
Sumber: http://java.lyracc.com/belajar/java-untuk-pemula/pengecualian-dan-pernyataan-try-catch
Siip lah
BalasHapus