Artikel ini berfokus pada beberapa kesalahpahaman dalam pemilihan dan penggunaan pengecualian Java. Saya berharap pembaca dapat menguasai beberapa poin dan prinsip penanganan pengecualian, dan memperhatikan ringkasan dan induksi. Hanya dengan menangani pengecualian kita dapat meningkatkan kualitas dasar pengembang, meningkatkan ketahanan sistem, meningkatkan pengalaman pengguna, dan meningkatkan nilai produk.
Kesalahpahaman 1. Pilihan abnormal
Gambar 1. Klasifikasi Anomali
Gambar 1 menjelaskan struktur pengecualian. Faktanya, kita semua tahu bahwa kelainan dibagi untuk mendeteksi kelainan dan kelainan yang tidak mendeteksi, tetapi dalam praktiknya, penerapan kedua kelainan ini bingung. Karena pengecualian yang tidak terdeteksi mudah digunakan, banyak pengembang berpikir bahwa deteksi pengecualian tidak berguna. Faktanya, skenario aplikasi abnormal dapat diringkas sebagai berikut:
1. Kode panggilan tidak dapat terus dieksekusi dan perlu segera diakhiri. Ada terlalu banyak kemungkinan untuk situasi ini, seperti server tidak terhubung, parameternya tidak benar, dll. Pengecualian yang tidak terdeteksi berlaku pada saat-saat ini, dan tidak perlu memanggil penangkapan dan pemrosesan kode yang eksplisit, dan kodenya ringkas dan jelas.
2. Kode panggilan membutuhkan pemrosesan dan pemulihan lebih lanjut. Jika SQLException didefinisikan sebagai pengecualian yang tidak terdeteksi, pengembang secara alami akan percaya bahwa SQLException tidak memerlukan memanggil penangkapan eksplisit dan pemrosesan kode, yang akan mengarah pada situasi serius seperti tidak menutup koneksi, tidak mengembalikan transaksi, dan data kotor dalam DB. Justru karena SQlexception didefinisikan sebagai mendeteksi pengecualian bahwa pengembang akan didorong untuk secara eksplisit menangkap dan membersihkan sumber daya setelah kode menghasilkan pengecualian. Tentu saja, setelah membersihkan sumber daya, Anda dapat terus melemparkan pengecualian yang tidak terdeteksi untuk mencegah pelaksanaan program. Menurut pengamatan dan pemahaman, deteksi pengecualian sebagian besar dapat diterapkan pada kelas alat. Java Learning Group 669823128
Kesalahpahaman 2: Menampilkan pengecualian langsung pada halaman atau klien.
Adalah umum untuk mencetak pengecualian langsung di sisi klien. Mengambil JSP sebagai contoh, setelah kode berjalan, wadah mencetak informasi tumpukan pengecualian langsung pada halaman secara default. Bahkan, dari perspektif pelanggan, setiap pengecualian tidak memiliki signifikansi praktis, dan sebagian besar pelanggan tidak dapat memahami informasi pengecualian sama sekali. Pengembangan perangkat lunak juga harus mencoba menghindari menyajikan pengecualian langsung kepada pengguna.
Daftar 1
<strong> Paket </strong> com.ibm.dw.sample.exception;/*** runtimeException khusus* Tambahkan atribut kode kesalahan*/<strong> public </strong> <strong> kelas </strong> <strong> runimeException </strong> <strong> meluas </strong> <strong> <strong> Java </Strong>. // kode kesalahan default <strong> publik </strong> <strong> statis </strong> <strong> final </strong> integer generik = 1000000; // kode kesalahan <strong> private </strong> Integer errorCode; <strong> publik </strong> <strong> runimeException </strong> (integer errorCode, penyebab yang dapat dilempar) {<strong> ini </strong> (errorCode, <strong> null </strong>, penyebab); } <strong> public </strong> <strong> runtimeException </strong> (pesan string, penyebab yang dapat dilewati) {// Gunakan kode kesalahan umum <strong> </strong> ini (generik, pesan, penyebab); } <strong> publik </strong> <strong> runtimeException </strong> (integer errorCode, pesan string, penyebab yang dapat dilempar) {<strong> super </strong> (pesan, penyebab); <strong> ini </strong> .ErrorCode = errorCode; } <strong> publik </strong> integer <strong> getErrorCode </strong> () {<strong> return </strong> errorCode; }}Seperti yang ditunjukkan kode contoh, perkenalkan kode kesalahan ke dalam pengecualian. Setelah pengecualian terjadi, kami hanya menyajikan kode kesalahan pengecualian kepada pengguna, atau mengubah kode kesalahan menjadi prompt yang lebih mudah dimengerti. Bahkan, kode kesalahan di sini juga berisi fungsi lain, dan pengembang juga dapat secara akurat mengetahui jenis pengecualian apa yang terjadi berdasarkan kode kesalahan.
Kesalahpahaman 3: Polusi Hirarki Kode
Kami sering membagi kode menjadi hierarki yang berbeda seperti layanan, logika bisnis, DAO, dll. Lapisan DAO akan berisi metode melempar pengecualian, seperti yang ditunjukkan dalam Listing 2:
Daftar 2
<strong> publik </strong> Pelanggan <strong> retrievecustomerbyid </strong> (ID panjang) <strong> throw </strong> sqlexception {// query database berdasarkan id}Sekilas, tidak ada masalah dengan kode di atas, tetapi jika Anda berpikir dengan hati -hati dari perspektif kopling desain, sqlexception di sini mencemari kode panggilan atas. Lapisan panggilan perlu secara eksplisit menggunakan TRY-Catch untuk ditangkap, atau lebih jauh melemparkannya ke tingkat yang lebih tinggi. Menurut prinsip isolasi desain, kita dapat memodifikasinya dengan tepat menjadi:
Daftar 3
<strong>public</strong> Customer <strong>retrieveCustomerById</strong>(Long id) { <strong>try</strong>{ //Query the database based on ID}<strong>catch</strong>(SQLException e){ //Use non-detected exception encapsulation to detect exceptions, reduce hierarchical coupling<strong>throw</strong> <strong>new</strong> RunimeException (sqlerrorcode, e); } <strong> akhirnya </strong> {// tutup koneksi dan membersihkan sumber daya}}Kesalahpahaman 4: Abaikan pengecualian
Penanganan pengecualian berikut hanya menghasilkan pengecualian ke konsol, dan itu tidak masuk akal. Selain itu, pengecualian muncul di sini dan program tidak mengganggu, dan kode panggilan terus dieksekusi, menghasilkan lebih banyak pengecualian.
Daftar 4
<strong> public </strong> <strong> batal </strong> <strong> retrieveObjectById </strong> (ID panjang) {<strong> coba </strong> {//..webone yang melempar sqlexception} <strong> tangkap </strong> (sqlexception ex) {/** siapa saja yang tahu itu tahu bahwa consception. * Di lingkungan produksi, tumpukan kesalahan harus output ke log. * Dan program terus mengeksekusi setelah proses penangkapan, yang akan menyebabkan masalah lebih lanjut*/ ex.printstacktrace (); }}Dapat direkonstruksi:
Daftar 5
<strong>public</strong> <strong>void</strong> <strong>retrieveObjectById</strong>(Long id){ <strong>try</strong>{ //..some code that throws SQLException } <strong>catch</strong>(SQLException ex){ <strong>throw</strong> <strong>new</strong> RuntimeException("Exception <strong>in</strong> retrieveObjectById ", ex); } <strong> akhirnya </strong> {// Bersihkan hasil, pernyataan, koneksi dll}}Kesalahpahaman ini relatif mendasar, dan dalam keadaan normal, Anda tidak akan membuat kesalahan tingkat rendah ini.
Kesalahpahaman 5: Sertakan pengecualian dalam blok pernyataan loop
Seperti yang ditunjukkan dalam kode berikut, pengecualian terkandung dalam blok pernyataan Loop.
Daftar 6
<strong> untuk </strong> (<strong> int </strong> i = 0; i <100; i ++) {<strong> coba </strong> {} <strong> catch </strong> (xxxException e) {//…. }}Kita semua tahu bahwa penanganan pengecualian menempati sumber daya sistem. Sekilas, semua orang berpikir bahwa mereka tidak akan melakukan kesalahan seperti itu. Dari perspektif lain, sebuah loop dieksekusi di Kelas A, dan metode Kelas B dipanggil dalam loop, tetapi metode yang dipanggil di Kelas B berisi blok pernyataan seperti TRY-Catch. Hirarki kelas telah memudar, dan kodenya persis sama seperti di atas.
Kesalahpahaman 6: Gunakan pengecualian untuk menangkap semua pengecualian potensial
Beberapa jenis pengecualian dilemparkan selama pelaksanaan suatu metode. Demi kesederhanaan kode, pengecualian kelas dasar digunakan untuk menangkap semua pengecualian potensial, seperti yang ditunjukkan dalam contoh berikut:
Daftar 7
<strong>public</strong> <strong>void</strong> <strong>retrieveObjectById</strong>(Long id){ <strong>try</strong>{ //…Code call that throws IOException //…Code call that throws SQLException}<strong>catch</strong>(Exception e){ // Here all potential exceptions caught by the base class Exception. Jika beberapa tingkatan menangkap ini, informasi yang valid dari pengecualian asli akan hilang <strong> lemparan </strong> <strong> baru </strong> runimeException ("Exception <strong> di </strong> retrieveObjectById", e); }}Dapat direkonstruksi
Daftar 8
<strong>public</strong> <strong>void</strong> <strong>retrieveObjectById</strong>(Long id){ <strong>try</strong>{ //..some code that throws RuntimeException, IOException, SQLException }<strong>catch</strong>(IOException e){ // Just catch IOException <strong>throw</strong> <strong> baru </strong> runtimeException (/*Tentukan kode kesalahan yang sesuai dengan ioException di sini*/kode, "Pengecualian <strong> di </strong> retrieveObjectById", e); } <strong> tangkap </strong> (sqlexception e) {// Cukup tangkap sqlexception <strong> lemparan </strong> <strong> baru </strong> runimeException (/*Tentukan kode kesalahan yang sesuai dengan sqlexception di sini*/kode, "pengecualian <strong> di </strong> retrieveBjectByid", e), "Exception <strong> di </strong> retrieveBjectByid", e), " }}Kesalahpahaman 7: Enkapsulasi multi-level melempar pengecualian yang tidak terdeteksi
Jika kami selalu bersikeras bahwa berbagai jenis pengecualian harus menggunakan pernyataan penangkapan yang berbeda, maka sebagian besar contoh dapat melewati bagian ini. Namun, jika hanya satu bagian dari panggilan kode yang akan melemparkan lebih dari satu pengecualian, seringkali tidak perlu menulis pernyataan tangkapan untuk setiap jenis pengecualian yang berbeda. Untuk pengembangan, pengecualian apa pun sudah cukup untuk menjelaskan masalah spesifik program.
Daftar 9
<strong> coba </strong> {// runtimeException, ioexeption atau lainnya dapat dilemparkan; // Perhatikan perbedaan antara di sini dan salah paham enam, berikut adalah sepotong kode yang melempar beberapa pengecualian. Di atas adalah beberapa segmen kode, masing -masing melempar pengecualian yang berbeda} <strong> tangkap </strong> (<strong> pengecualian </strong> e) {// Seperti biasa, konversi pengecualian menjadi runtimeeException, tetapi E di sini sebenarnya adalah contoh dari runtimeexception, dan telah dikenakan dalam kode sebelumnya. RunimeException (/**/code,/**/, e);}Jika kita mengonversi semua pengecualian menjadi runtimeException seperti yang ditunjukkan pada contoh di atas, maka ketika jenis pengecualian sudah runtimeException, kami melakukan enkapsulasi lain. RuntimeException dienkapsulasi ulang lagi, dan informasi yang valid yang dibawa oleh runimeException asli hilang.
Solusinya adalah bahwa kami dapat menambahkan cek yang relevan ke kelas RuntimeException untuk mengonfirmasi bahwa parameter yang dapat dilemparkan bukanlah instance dari runtimeException. Jika demikian, salin atribut yang sesuai ke instance yang baru dibuat. Atau menggunakan blok pernyataan tangkapan yang berbeda untuk menangkap runtimeeException dan pengecualian lainnya. Metode Preferensi Pribadi 1, manfaatnya jelas.
Kesalahpahaman 8: Pengecualian pencetakan multi-level
Mari kita lihat contoh berikut, yang mendefinisikan 2 Kode Kelas A dan B. Kelas B disebut di Kelas A, dan kedua Kelas A dan Kelas B menangkap dan mencetak pengecualian.
Daftar 10
<strong> public </strong> <strong> kelas </strong> <strong> a </strong> {<strong> private </strong> <strong> statis </strong> Logger Logger = LoggerFactory.getLogger (a.class); <strong> public </strong> <strong> void </strong> <strong> proses </strong> () {<strong> coba </strong> {// Instantiate Class B, Anda dapat mengubah ke metode injeksi lain seperti B B = <strong> baru </strong> B (); B. Process (); // Kode lain dapat menyebabkan pengecualian} <strong> Catch </strong> (XXXException e) {// Jika metode proses kelas B melempar pengecualian, pengecualian akan ada di B kelas dicetak, dan juga akan dicetak di sini, jadi Logger.Error (e); <strong> throw </strong> <strong> baru </strong> runtimeException (/*kode kesalahan*/errorCode,/*informasi pengecualian*/msg, e); }}} <strong> publik </strong> <strong> kelas </strong> <strong> B </strong> {<strong> private </strong> <strong> statis </strong> logger = loggerFactory.getLogger (b.class); <strong> publik </strong> <strong> void </strong> <strong> proses </strong> () {<strong> coba </strong> {// kode yang mungkin melempar pengecualian} <strong> catch </strong> (xxxexception e) {logger.error (e); <strong> throw </strong> <strong> baru </strong> runtimeException (/*kode kesalahan*/errorCode,/*informasi pengecualian*/msg, e); }}}Pengecualian yang sama akan dicetak 2 kali. Jika levelnya sedikit lebih rumit, sakit kepala untuk tidak mempertimbangkan kinerja sistem pencetakan log, dan cukup menemukan masalah spesifik dalam log pengecualian.
Faktanya, pencetakan log hanya membutuhkan penangkapan dan pencetakan di lapisan luar kode. Pencetakan pengecualian juga dapat ditulis sebagai AOP dan ditenun ke lapisan luar bingkai.
Kesalahpahaman 9: Masalah bahwa informasi yang terkandung dalam pengecualian tidak dapat sepenuhnya ditemukan
Pengecualian tidak hanya memungkinkan pengembang untuk mengetahui apa yang salah, tetapi lebih sering pengembang juga perlu tahu apa yang menyebabkan masalah. Kita tahu bahwa Java .lang.Exception memiliki konstruktor parameter tipe string, dan string ini dapat disesuaikan menjadi informasi prompt yang mudah dipahami.
Pengembang informasi khusus sederhana hanya dapat mengetahui di mana pengecualian muncul, tetapi dalam banyak kasus, pengembang perlu mengetahui lebih banyak tentang parameter apa yang menyebabkan pengecualian seperti itu. Pada saat ini, kita perlu menambahkan informasi parameter dari panggilan metode ke informasi khusus. Contoh berikut hanya mencantumkan kasus satu parameter. Dalam hal beberapa parameter, Anda dapat menulis kelas alat untuk mengatur string tersebut.
Daftar 11
public <strong>void</strong> retrieveObjectById(Long id){ <strong>try</strong>{ //..some code that throws SQLException }<strong>catch</strong>(SQLException ex){ //Add parameter information to exception information <strong>throw</strong> <strong>new</strong> RuntimeException("Exception <strong>in</strong> retrieveObjectById <strong> dengan </strong> ID objek: "+ id, ex); }}Kesalahpahaman 10: Tidak dapat memprediksi potensi kelainan
Selama proses penulisan kode, karena kurangnya pemahaman yang mendalam tentang kode panggilan, tidak mungkin untuk secara akurat menentukan apakah kode yang dipanggil akan menghasilkan pengecualian, sehingga pemrosesan diabaikan. Setelah bug produksi dihasilkan, saya ingat bahwa saya harus menambahkan pengambilan pengecualian ke sepotong kode tertentu, dan saya bahkan tidak bisa secara akurat menunjukkan penyebab pengecualian. Ini membutuhkan pengembang tidak hanya tahu apa yang mereka lakukan, tetapi juga untuk mengetahui sebanyak mungkin apa yang telah dilakukan orang lain dan hasil apa yang mungkin disebabkan, dan untuk mempertimbangkan proses pemrosesan seluruh aplikasi dari perspektif global. Ide -ide ini akan memengaruhi penulisan dan pemrosesan kode kami.
Kesalahpahaman 11: Penggunaan campuran dari beberapa perpustakaan log pihak ketiga
Saat ini, ada semakin banyak perpustakaan log pihak ketiga Java. Berbagai kerangka kerja akan diperkenalkan dalam proyek besar, dan kerangka kerja ini akan bergantung pada implementasi perpustakaan log yang berbeda. Masalah yang paling merepotkan adalah tidak memperkenalkan semua perpustakaan log yang diperlukan, masalahnya adalah bahwa pustaka log yang diperkenalkan sendiri tidak sesuai. Jika mungkin mudah dipecahkan pada tahap awal proyek, Anda dapat memperkenalkan kembali semua pustaka log dalam kode Anda sesuai kebutuhan, atau berubah menjadi kerangka kerja. Tetapi biaya semacam ini tidak terjangkau untuk setiap proyek, dan semakin besar risikonya saat proyek berlangsung.
Bagaimana kita dapat secara efektif menghindari masalah serupa? Sebagian besar kerangka kerja sekarang telah memperhitungkan masalah serupa. Mereka dapat mengonfigurasi file properti atau XML, parameter, atau kelas implementasi log pemindaian runtime di pustaka LIB, dan hanya ketika aplikasi berjalan, kami dapat menentukan pustaka log spesifik mana yang akan diterapkan.
Faktanya, berdasarkan prinsip tidak memerlukan beberapa tingkat pencetakan log, kita dapat menyederhanakan banyak kelas yang awalnya disebut kode pencetakan log. Dalam banyak kasus, kita dapat menggunakan pencegat atau filter untuk mencetak log untuk mengurangi biaya pemeliharaan dan migrasi kode.
Kesimpulan
Di atas adalah pengalaman dan ringkasan pribadi murni. Segalanya dialektis, dan tidak ada prinsip absolut. Prinsip yang paling efektif cocok untuk Anda. Saya berharap penjelasan dan analisis di atas dapat membantu Anda.