Pengecualian Java adalah mekanisme konsistensi yang disediakan oleh Java untuk mengidentifikasi dan menanggapi kesalahan.
Mekanisme pengecualian Java dapat memisahkan kode penanganan pengecualian dalam program dari kode bisnis normal, memastikan bahwa kode program lebih elegan dan meningkatkan ketahanan program. Saat menggunakan pengecualian secara efektif, pengecualian dapat dengan jelas menjawab ketiga pertanyaan ini: jenis pengecualian menjawab "apa" dan jawaban jejak tumpukan pengecualian "di mana" dan informasi pengecualian menjawab "mengapa" dilemparkan.
Beberapa kata kunci yang digunakan dalam mekanisme pengecualian Java: coba, tangkap, akhirnya, lempar, melempar.
| Kata kunci | menjelaskan |
|---|---|
| mencoba | Digunakan untuk mendengarkan. Letakkan kode untuk didengarkan (kode yang mungkin melempar pengecualian) di blok pernyataan coba. Ketika pengecualian terjadi di blok pernyataan coba, pengecualian dilemparkan. |
| menangkap | Digunakan untuk menangkap pengecualian. Catch digunakan untuk menangkap pengecualian yang terjadi di blok pernyataan try. |
| Akhirnya | Akhirnya blok pernyataan akan selalu dieksekusi. Ini terutama digunakan untuk mendaur ulang sumber daya material (seperti koneksi basis data, koneksi jaringan dan file disk) yang dibuka di blok coba. Hanya setelah pelaksanaan blok akhirnya akan mengembalikan atau melempar pernyataan di blok mencoba atau menangkap. Jika pernyataan akhir seperti pengembalian atau lemparan digunakan, mereka tidak akan melompat kembali ke eksekusi dan akan berhenti secara langsung. |
| melemparkan | Digunakan untuk melempar pengecualian. |
| melempar | Digunakan dalam tanda tanda tangan metode untuk menyatakan pengecualian yang mungkin dilemparkan oleh metode ini. |
kelas publik demo1 {public static void main (string [] args) {try {int i = 10/0; System.out.println ("i ="+i); } catch (ArithMeticException e) {System.out.println ("Catching Exception"); System.out.println ("E.GetMessage ():" + e.getMessage ()); System.out.println ("e.tostring ():" + e.tostring ()); System.out.println ("E.PrintStackTrace ():"); e.printstacktrace (); }}} Hasil Menjalankan:
Ditangkap Exceptione.getMessage (): / oleh Zeroe.tostring (): java.lang.arithmeticException: / oleh Zeroe.printstacktrace (): java.lang.arithmeticexception: / oleh Zero di demo1.main (demo1.java:6)
Deskripsi Hasil: Ada operasi dengan pembagi 0 di blok pernyataan coba, dan operasi akan melempar pengecualian java.lang.arithmeticException. Dengan tangkapan, pengecualian ditangkap.
Mengamati hasil yang kami temukan bahwa System.out.println ("i ="+i) tidak dieksekusi. Ini berarti bahwa setelah pengecualian terjadi di blok pernyataan coba, konten yang tersisa di blok pernyataan try tidak akan lagi dieksekusi.
Contoh 2: Memahami penggunaan dasar akhirnya
Berdasarkan "Contoh Satu", kami menambahkan pernyataan akhirnya.
kelas publik demo2 {public static void main (string [] args) {try {int i = 10/0; System.out.println ("i ="+i); } catch (ArithMeticException e) {System.out.println ("Catching Exception"); System.out.println ("E.GetMessage ():" + e.getMessage ()); System.out.println ("e.tostring ():" + e.tostring ()); System.out.println ("E.PrintStackTrace ():"); e.printstacktrace (); } akhirnya {System.out.println ("Run akhirnya"); }}} Hasil Menjalankan:
Tangkap Exceptione.getMessage (): / oleh Zeroe.tostring (): java.lang.arithmeticException: / oleh Zeroe.printstacktrace (): java.lang.arithmeticException: / dengan nol di demo2.main (demo2.java:6)
Hasil: Akhirnya blok pernyataan dieksekusi.
Contoh 3: Memahami penggunaan dasar lemparan dan lemparan
Lemparan digunakan untuk menyatakan pengecualian yang dilempar, sementara lemparan digunakan untuk melempar pengecualian.
kelas myException memperluas pengecualian {public myException () {} public myException (string msg) {super (msg); }} public class demo3 {public static void main (string [] args) {try {test (); } catch (myException e) {System.out.println ("Catch My Exception"); e.printstacktrace (); }} public static void test () melempar myException {coba {int i = 10/0; System.out.println ("i ="+i); } catch (arithMeticException e) {lempar myException baru ("this is myexception"); }}} Hasil Menjalankan:
Tangkap ExceptionMyException saya: Ini adalah MyException di Demo3.Test (demo3.java:24) di demo3.main (demo3.java:13)
Hasil: MyException adalah subkelas yang diwarisi dari pengecualian. Pengecualian ArithmeticException (Divider adalah 0) dihasilkan dalam blok pernyataan try test (), dan pengecualian ditangkap dalam tangkapan; Kemudian pengecualian MyException dilemparkan. Metode utama () menangkap myException yang dilemparkan dalam tes ().
Kerangka Pengecualian Java
Diagram Arsitektur Pengecualian Java:
1. Throwable
Throwable adalah superclass dari semua kesalahan atau pengecualian dalam bahasa Java.
Throwable berisi dua subclass: kesalahan dan pengecualian. Mereka biasanya digunakan untuk menunjukkan kelainan telah terjadi.
Throwable berisi snapshot dari utas yang mengeksekusi tumpukan saat utasnya dibuat. Ini menyediakan antarmuka seperti printStackTrace () untuk mendapatkan informasi seperti data stack jejak.
2. Pengecualian
Pengecualian dan subkelasnya adalah bentuk yang dapat dilemparkan yang menunjukkan kondisi yang ingin ditangkap oleh aplikasi yang masuk akal.
3. RuntimeException
RuntimeException adalah superclass yang dapat melemparkan pengecualian selama operasi normal mesin virtual Java.
Kompiler tidak memeriksa pengecualian runimeException. Misalnya, ketika pembagi adalah nol, pengecualian ArithmeticException dilemparkan. RuntimeException adalah superclass dari ArithmeticException. Ketika kode memiliki pembagi nol, itu juga dapat dikompilasi jika "tidak dilemparkan melalui deklarasi lemparan" atau "tidak ditangani melalui cobalah ... tangkap ...". Inilah yang kami katakan "Kompiler tidak akan memeriksa pengecualian runimeException"!
Jika kode menghasilkan pengecualian runimeException, itu perlu dihindari dengan memodifikasi kode. Misalnya, jika pembagi nol, Anda perlu menghindari situasi ini melalui kode!
4. Kesalahan
Seperti pengecualian, kesalahan juga merupakan subclass dari Throwable. Digunakan untuk menunjukkan masalah serius yang tidak boleh dilakukan oleh aplikasi yang masuk akal, dan sebagian besar kesalahan tersebut adalah kondisi yang luar biasa.
Seperti RuntimeException, kompiler tidak akan memeriksa kesalahan.
Java membagi struktur yang dapat dilempar menjadi tiga jenis: pengecualian diperiksa (pengecualian yang diperiksa), pengecualian runtime (runtimeException) dan kesalahan (kesalahan).
(1) Pengecualian runtime
Definisi: RuntimeException dan subclass -nya disebut pengecualian runtime.
Fitur: Kompiler Java tidak akan memeriksanya. Dengan kata lain, ketika pengecualian seperti itu dapat terjadi dalam program, jika "tidak dilemparkan melalui deklarasi lemparan" atau "tidak tertangkap dengan pernyataan mencoba-tangkapan", itu masih akan dikompilasi dan disahkan. Sebagai contoh, pengecualian ArithMeticException yang dihasilkan ketika pembagi nol, IndexOutOfBoundsException Exception dihasilkan ketika array di luar batas, pengecualian Exception Exception yang dihasilkan oleh mekanisme gagal-gagal, dll., Semua pengecualian runtime.
Meskipun kompiler Java tidak memeriksa pengecualian runtime, kami juga dapat mendeklarasikan dan melemparkannya melalui lemparan, atau menangkapnya melalui TRY-Catch.
Jika pengecualian runtime dihasilkan, itu perlu dihindari dengan memodifikasi kode. Misalnya, jika pembagi nol, Anda perlu menghindari situasi ini melalui kode!
(2) pengecualian diperiksa
Definisi: Kelas pengecualian itu sendiri, dan subclass lain dalam subclass Exception kecuali "Runtime Exception" semuanya dianggap pengecualian yang diperiksa.
Fitur: Kompiler Java akan memeriksanya. Pengecualian seperti itu dinyatakan dan dilemparkan melalui lemparan atau ditangkap melalui TRY-Catch, jika tidak mereka tidak dapat dikompilasi. Misalnya, ClonenotsupportedException adalah pengecualian yang diperiksa. Ketika suatu objek dikloning melalui antarmuka klon (), dan kelas objek yang sesuai tidak mengimplementasikan antarmuka yang dapat dikloning, clonenotsupportedException akan dilemparkan.
Pengecualian yang sedang diperiksa biasanya dapat dipulihkan.
(3) Kesalahan
Definisi: Kelas kesalahan dan subkelasnya.
Fitur: Seperti pengecualian runtime, kompiler tidak akan memeriksa kesalahan.
Kesalahan terjadi ketika ada sumber daya yang tidak memadai, kegagalan kendala, atau kondisi lain yang tidak dapat terus dijalankan oleh program lain. Program itu sendiri tidak dapat memperbaiki kesalahan ini. Misalnya, VirtualMachineError adalah kesalahan.
Menurut Java Convention, kita tidak boleh menerapkan subkelas kesalahan baru!
Untuk tiga struktur di atas, mana yang harus kita lakukan pengecualian atau kesalahan? Rekomendasi yang diberikan dalam "Java yang efektif" adalah: Gunakan pengecualian yang diperiksa untuk kondisi yang dapat dipulihkan, dan gunakan pengecualian runtime untuk kesalahan program.
Beberapa saran tentang penanganan pengecualian
Pasal 1: Gunakan pengecualian hanya untuk situasi abnormal
Rekomendasi: Pengecualian hanya boleh digunakan untuk kondisi abnormal, dan mereka tidak boleh digunakan untuk aliran kontrol normal.
Penjelasan dibuat dengan membandingkan dua kode di bawah ini.
Kode 1
coba {int i = 0; while (true) {arr [i] = 0; i ++; }} catch (IndexOutOfBoundsException e) {} kode 2FOR (int i = 0; i <arr.length; i ++) {arr [i] = 0;} Tujuan dari kedua kode ini adalah untuk mengulangi array ARR dan menetapkan nilai setiap elemen dalam array ke 0. Kode 1 diakhiri dengan pengecualian, yang tampaknya sangat sulit untuk dipahami, Kode 2 berakhir dengan batas array. Kita harus menghindari menggunakan kode 1 karena tiga alasan utama:
Desain asli dari mekanisme pengecualian adalah untuk situasi abnormal, sehingga sedikit implementasi JVM mencoba mengoptimalkan kinerja mereka. Oleh karena itu, overhead dari menciptakan, melempar, dan menangkap pengecualian mahal.
Menempatkan kode dalam pengembalian try-catch mencegah JVM dari menerapkan optimasi spesifik tertentu yang mungkin dilakukan.
Pola standar array yang melintasi tidak mengarah pada pemeriksaan yang berlebihan, dan beberapa implementasi JVM modern akan mengoptimalkannya.
Faktanya, mode berbasis pengecualian jauh lebih lambat daripada mode standar. Kode tes adalah sebagai berikut:
nasihat kelas publik1 {private static int [] arr = new int [] {1,2,3,4,5}; Ukuran int statis pribadi = 10000; public static void main (string [] args) {long s1 = system.currentTimeMillis (); untuk (int i = 0; i <size; i ++) endbyrange (arr); long e1 = system.currentTimemillis (); System.out.println ("EndByRange Time:"+(E1-S1)+"MS"); long s2 = system.currentTimemillis (); untuk (int i = 0; i <size; i ++) EndbyException (ARR); long e2 = system.currentTimemillis (); System.out.println ("EndByException Time:"+(E2-S2)+"MS"); } // Lintasan array ARR: private static void endbyException (int [] arr) {coba {int i = 0; while (true) {arr [i] = 0; i ++; //System.out.println("endbyRange: arr ["+i+"] = "+arr [i]); }} catch (IndexOutOfBoundsException e) {}} // Transfer array ARR: private static void endbyrange (int [] arr) {for (int i = 0; i <arr.length; i ++) {arr [i] = 0; //System.out.println("endByException: arr ["+i+"] = "+arr [i]); }}} Hasil Menjalankan:
Waktu EndbyRange: 8MSendByException Time: 16ms
Hasilnya menunjukkan bahwa kecepatan melintasi pengecualian jauh lebih lambat daripada melintasi array dengan cara biasa!
Pasal 2: Gunakan pengecualian yang diperiksa untuk kondisi yang dapat dipulihkan, dan gunakan pengecualian runtime untuk kesalahan program.
| abnormal | menjelaskan |
|---|---|
| Pengecualian runtime | Kelas RunimeException dan subclass -nya disebut pengecualian runtime. |
| Pengecualian yang diperiksa | Kelas pengecualian itu sendiri, serta subclass lain dalam pengecualian kecuali "pengecualian runtime" semuanya adalah pengecualian yang diperiksa. |
Perbedaannya adalah bahwa Java Compiler memeriksa "pengecualian yang diperiksa" dan tidak memeriksa "pengecualian runtime".
Dengan kata lain, untuk pengecualian yang diperiksa, itu dinyatakan dan dilemparkan melalui lemparan, atau ditangkap melalui TRY-Catch, jika tidak, itu tidak dapat dikompilasi. Untuk pengecualian runtime, jika mereka "tidak dilemparkan melalui deklarasi lemparan" atau "tidak tertangkap dengan pernyataan mencoba-tangkapan", mereka masih akan dikompilasi dan disahkan. Tentu saja, meskipun kompiler Java tidak memeriksa pengecualian runtime, kami juga dapat menjelaskan pengecualian melalui lemparan, atau menangkapnya melalui TRY-Catch.
RithMeticException (misalnya, pembagi adalah 0), IndexOutOfBoundsException (misalnya, array di luar batas), dll. Semua adalah pengecualian runtime. Untuk pengecualian ini, kita harus menghindarinya dengan memodifikasi kode. Untuk pengecualian yang diperiksa, program dapat dikembalikan untuk dijalankan melalui pemrosesan. Misalnya, misalkan bahwa karena pengguna tidak menyimpan sejumlah panggilan yang cukup, ia akan gagal ketika mencoba melakukan panggilan pada panggilan bayaran; dengan demikian melemparkan pengecualian yang diperiksa.
Pasal 3: Hindari penggunaan pengecualian yang tidak perlu
"Censored Exception" adalah fitur yang bagus dari Java. Berbeda dengan kode pengembalian, "pengecualian yang diperiksa" memaksa programmer untuk menangani kondisi pengecualian, sangat meningkatkan keandalan program.
Namun, penggunaan pengecualian yang diperiksa secara berlebihan dapat membuat API sangat tidak nyaman. Jika suatu metode melempar satu atau lebih pengecualian yang diperiksa, kode yang memanggil metode harus menangani pengecualian ini dalam satu atau lebih blok pernyataan tangkapan, atau mereka harus dilemparkan melalui deklarasi lemparan. Apakah itu ditangani melalui tangkapan atau dilemparkan melalui deklarasi lemparan, itu menambah beban yang tidak dapat diyakinkan untuk pemrogram.
Dua kondisi harus dipenuhi untuk "pengecualian yang diperiksa": pertama, bahkan jika API digunakan dengan benar, itu tidak dapat mencegah terjadinya kondisi pengecualian. Kedua, setelah pengecualian dihasilkan, pemrogram yang menggunakan API dapat mengambil tindakan yang berguna untuk memproses program.
Pasal 4: Coba gunakan pengecualian standar
Penggunaan kembali kode layak untuk advokasi, ini adalah aturan umum, dan pengecualian tidak terkecuali. Ada beberapa manfaat untuk menggunakan kembali pengecualian yang ada:
Pertama, itu membuat API Anda lebih mudah dipelajari dan digunakan karena konsisten dengan idiom yang menjadi akrab dengan programmer.
Kedua, untuk program yang menggunakan API ini, mereka lebih mudah dibaca karena tidak diisi dengan pengecualian yang tidak akrab bagi pemrogram.
Ketiga, semakin sedikit kelas pengecualian, semakin kecil penggunaan memori, dan semakin kecil waktu yang dihabiskan untuk mencetak ulang kelas -kelas ini.
Beberapa pengecualian standar Java sering digunakan pengecualian. Tabel berikut:
| abnormal | Gunakan kesempatan |
|---|---|
| IllegalargumentException | Nilai parameter tidak sesuai |
| IXCEPON ILLEGALSTATE | Parameternya tidak tepat |
| NullpointerException | Saat nol dinonaktifkan, nilai parameternya nol |
| IndexOutOfBoundsException | Subskrip melintasi batas |
| ConcurrentModificationException | Ketika modifikasi bersamaan dilarang, objek mendeteksi modifikasi bersamaan |
| UnsupportedOperationException | Metode objek itu tidak mendukung permintaan pelanggan |
Meskipun mereka adalah pengecualian yang paling umum digunakan kembali dari perpustakaan platform Java hingga saat ini, pengecualian lain juga dapat digunakan kembali dalam kondisi lisensi. Misalnya, jika Anda ingin mengimplementasikan objek aritmatika seperti bilangan atau matriks yang kompleks, akan sangat tepat untuk menggunakan kembali arithmeticException dan NumberFormatException. Jika pengecualian memenuhi kebutuhan Anda, jangan ragu untuk menggunakannya, tetapi Anda harus memastikan bahwa kondisi melempar pengecualian konsisten dengan kondisi yang dijelaskan dalam dokumentasi untuk pengecualian. Penggunaan kembali ini harus didasarkan pada semantik, bukan pada namanya!
Akhirnya, pastikan untuk menjelaskan bahwa tidak ada aturan yang harus diikuti ketika memilih pengecualian yang harus digunakan kembali. Misalnya, mengingat kasus objek kartu, misalkan ada metode untuk menangani operasi, dan parameternya (handsize) adalah jumlah kartu yang diberikan di tangan. Misalkan penelepon memberikan nilai dalam parameter ini lebih besar dari jumlah kartu yang tersisa untuk seluruh dek. Maka situasi ini dapat diartikan sebagai IllegalArgumentException (nilai handsize terlalu besar) atau IllegalStateException (objek kartu memiliki terlalu sedikit kartu relatif terhadap permintaan klien).
Pasal 5: Pengecualian yang dilemparkan harus cocok untuk abstraksi yang sesuai
Situasi ini bisa luar biasa jika pengecualian yang dilemparkan oleh metode tidak memiliki korelasi yang jelas dengan tugas yang dilakukannya. Ini sering terjadi ketika suatu metode melewati pengecualian yang dilemparkan oleh abstraksi tingkat rendah. Ketika ini terjadi, itu tidak hanya membingungkan, tetapi juga "polut" API tingkat tinggi.
Untuk menghindari masalah ini, implementasi tingkat tinggi harus menangkap pengecualian tingkat rendah dan melemparkan pengecualian yang dapat diperkenalkan sesuai dengan abstraksi tingkat tinggi. Praktik ini disebut "terjemahan pengecualian".
Misalnya, metode Java Collection Framework AbstractSequallist get () adalah sebagai berikut (berdasarkan JDK1.7.0_40):
public e get (int index) {coba {return listiterator (index) .next (); } catch (noSuchelementException exc) {throw indexOutofboundsException ("index:"+index); }}ListIterator (Indeks) akan mengembalikan objek ListIterator. Memanggil metode objek berikutnya () dapat melemparkan pengecualian Exception Nosuchelement. Dalam metode get (), melempar pengecualian NosuchelementException akan membingungkan. Jadi, dapatkan () menangkap NosuchelementException dan melempar IndexOutOfBoundsException. Artinya, setara dengan mengonversi NosuchelementException menjadi IndexOutOfBoundsException Exception.
Pasal 6: Pengecualian yang dilemparkan oleh setiap metode harus didokumentasikan
Untuk mendeklarasikan pengecualian yang diperiksa secara terpisah, dan gunakan tag @Throws Javadoc untuk secara akurat mencatat kondisi untuk setiap pengecualian yang akan dilemparkan.
Jika banyak metode dalam kelas melempar pengecualian yang sama untuk alasan yang sama, dapat diterima untuk melakukan dokumentasi untuk pengecualian ini dalam komentar dokumen kelas itu, daripada mendokumentasikan secara individual untuk setiap metode.
Pasal 7: Sertakan kegagalan dalam detail pesan penangkapan pesan
Singkatnya, ketika kami menyesuaikan atau melempar pengecualian, kami harus menyertakan informasi yang terkait dengan kegagalan.
Ketika suatu program gagal karena pengecualian yang tidak dibutuhkan, sistem akan secara otomatis mencetak jejak tumpukan pengecualian. Berisi representasi string dari pengecualian di trek tumpukan. Biasanya berisi nama kelas dari kelas pengecualian, bersama dengan pesan terperinci yang mengikuti.
Pasal 8: Berusaha keras untuk mempertahankan atomisitas dalam kegagalan
Ketika suatu objek melempar pengecualian, kami selalu berharap bahwa objek tetap dalam keadaan yang tersedia dengan baik. Ini sangat penting untuk pengecualian yang diperiksa, karena penelepon biasanya mengharapkan untuk pulih dari pengecualian yang diperiksa.
Secara umum, panggilan metode yang gagal harus menjaga objek dalam "keadaan sebelum dipanggil". Metode dengan atribut tersebut disebut "atom kegagalan". Dapat dipahami bahwa kegagalan masih mempertahankan atomisitas. Ada beberapa cara agar suatu objek mempertahankan "atomisitas yang gagal":
(1) Rancang objek yang tidak dapat diubah.
(2) Untuk metode yang melakukan operasi pada objek yang dapat berubah, cara paling umum untuk mendapatkan "atomisitas yang gagal" adalah dengan memeriksa validitas parameter sebelum melakukan operasi. Sebagai berikut (metode pop di stack.java):
objek publik pop () {if (size == 0) Lempar baru kosongStackException (); Hasil objek = elemen [-ukuran]; elemen [ukuran] = null; Hasil pengembalian;} (3) Mirip dengan metode sebelumnya, pemrosesan perhitungan dapat disesuaikan sehingga kemungkinan kegagalan bagian perhitungan terjadi sebelum keadaan objek dimodifikasi.
(4) Tulis kode pemulihan untuk menjelaskan kegagalan selama operasi dan membuat objek kembali ke keadaan sebelum operasi dimulai.
(5) Lakukan operasi pada salinan sementara objek, dan setelah operasi selesai, salin hasilnya dalam salinan sementara ke objek asli.
Sementara "mempertahankan atomisitas yang gagal dari suatu objek" adalah tujuan yang diinginkan, itu tidak selalu mungkin. Misalnya, jika beberapa utas mencoba mengakses objek secara bersamaan tanpa mekanisme sinkronisasi yang tepat, objek tersebut dapat dibiarkan dalam keadaan yang tidak konsisten.
Bahkan dalam situasi di mana "atomisitas yang gagal" dapat dicapai, itu tidak selalu diharapkan. Untuk beberapa operasi, ini dapat secara signifikan meningkatkan overhead atau kompleksitas.
Aturan umum adalah: sebagai bagian dari spesifikasi metode, tidak ada pengecualian yang harus mengubah status objek sebelum memanggil metode. Jika aturan ini dilanggar, dokumentasi API harus dengan jelas menunjukkan keadaan apa objek akan berada.
Pasal 9: Jangan Abaikan Pengecualian
Ketika seorang desainer API menyatakan bahwa metode akan melempar pengecualian, mereka mencoba menggambarkan sesuatu. Jadi, tolong jangan abaikan! Kode untuk mengabaikan pengecualian adalah sebagai berikut:
coba {...} catch (someexception e) {} Blok tangkapan kosong akan membuat pengecualian gagal mencapai tujuan yang tepat. Tujuan dari pengecualian adalah untuk memaksa Anda untuk berurusan dengan kondisi abnormal. Mengabaikan pengecualian seperti mengabaikan sinyal alarm kebakaran - jika sinyal alarm kebakaran dimatikan, maka tidak ada yang akan melihat sinyal alarm kebakaran ketika kebakaran nyata terjadi. Jadi, setidaknya blok tangkapan harus berisi deskripsi yang menjelaskan mengapa pantas untuk mengabaikan pengecualian ini.