Apakah menangkap basah merupakan praktik yang buruk Throwable
?
Contohnya seperti ini:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Apakah ini praktik yang buruk atau kita harus sespesifik mungkin?
Apakah menangkap basah merupakan praktik yang buruk Throwable
?
Contohnya seperti ini:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Apakah ini praktik yang buruk atau kita harus sespesifik mungkin?
Jawaban:
Anda harus sespesifik mungkin. Jika tidak, serangga yang tidak terduga mungkin akan merayap dengan cara ini.
Selain itu, Throwable
selimut Error
juga dan itu biasanya tidak ada gunanya kembali . Anda tidak ingin menangkap / mengatasinya, Anda ingin program Anda segera mati sehingga Anda dapat memperbaikinya dengan baik.
Ini ide yang buruk. Bahkan, menangkap Exception
biasanya merupakan ide yang buruk. Mari pertimbangkan sebuah contoh:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
Sekarang, katakanlah getUserInput () memblokir untuk sementara, dan thread lain menghentikan thread Anda dengan cara yang paling buruk (ia memanggil thread.stop ()). Blok tangkapan Anda akan menemukan ThreadDeath
Kesalahan. Ini sangat buruk. Perilaku kode Anda setelah menangkap Exception tersebut sebagian besar tidak ditentukan.
Masalah serupa terjadi dengan menangkap Exception. Mungkin getUserInput()
gagal karena InterruptException, atau pengecualian izin ditolak saat mencoba mencatat hasil, atau segala macam kegagalan lainnya. Anda tidak tahu apa yang salah, karena itu, Anda juga tidak tahu bagaimana cara mengatasi masalah tersebut.
Anda memiliki tiga opsi yang lebih baik:
1 - Tangkap persis Pengecualian yang Anda tahu cara menanganinya:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
inputNumber = 10; //Default, user did not enter valid number
}
2 - Lempar kembali pengecualian yang Anda hadapi dan tidak tahu cara menanganinya:
try {
doSomethingMysterious();
} catch(Exception e) {
log.error("Oh man, something bad and mysterious happened",e);
throw e;
}
3 - Gunakan blok terakhir sehingga Anda tidak perlu ingat untuk melempar ulang:
Resources r = null;
try {
r = allocateSomeResources();
doSomething(r);
} finally {
if(r!=null) cleanUpResources(r);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Ini akan dicatat dalam satu pengecualian bagus dengan 10 penyebab.
Perlu diketahui juga bahwa saat menangkap Throwable
, Anda juga bisa menangkap InterruptedException
yang membutuhkan perlakuan khusus. Lihat Dealing with InterruptedException untuk detail selengkapnya.
Jika Anda hanya ingin menangkap pengecualian yang tidak dicentang, Anda juga dapat mempertimbangkan pola ini
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
Dengan cara ini, saat Anda memodifikasi kode dan menambahkan panggilan metode yang dapat memunculkan pengecualian yang dicentang, kompilator akan mengingatkan Anda tentang itu dan kemudian Anda dapat memutuskan apa yang harus dilakukan untuk kasus ini.
langsung dari javadoc kelas Error (yang merekomendasikan untuk tidak menangkap ini):
* An <code>Error</code> is a subclass of <code>Throwable</code>
* that indicates serious problems that a reasonable application
* should not try to catch. Most such errors are abnormal conditions.
* The <code>ThreadDeath</code> error, though a "normal" condition,
* is also a subclass of <code>Error</code> because most applications
* should not try to catch it.
* A method is not required to declare in its <code>throws</code>
* clause any subclasses of <code>Error</code> that might be thrown
* during the execution of the method but not caught, since these
* errors are abnormal conditions that should never occur.
*
* @author Frank Yellin
* @version %I%, %G%
* @see java.lang.ThreadDeath
* @since JDK1.0
Ini bukan praktik yang buruk jika Anda sama sekali tidak dapat mengeluarkan gelembung pengecualian dari suatu metode.
Ini praktik yang buruk jika Anda benar-benar tidak dapat menangani pengecualian. Lebih baik menambahkan "lemparan" ke tanda tangan metode daripada hanya menangkap dan melempar ulang atau, lebih buruk lagi, membungkusnya dalam RuntimeException dan lemparan ulang.
Throwable
contoh - misalnya untuk pencatatan pengecualian khusus.
Catching Throwable terkadang diperlukan jika Anda menggunakan pustaka yang melempar Kesalahan secara berlebihan, jika tidak, pustaka Anda dapat mematikan aplikasi Anda.
Namun, akan lebih baik dalam keadaan ini untuk menentukan hanya kesalahan spesifik yang dilemparkan oleh perpustakaan, daripada semua Throwable.
Throwable adalah kelas dasar untuk semua kelas yang dapat dilempar (tidak hanya pengecualian). Ada sedikit yang dapat Anda lakukan jika Anda menangkap OutOfMemoryError atau KernelError (lihat Kapan menangkap java.lang.Error? )
menangkap Pengecualian seharusnya cukup.
itu tergantung pada logika Anda atau untuk lebih spesifik pada pilihan / kemungkinan Anda. Jika ada pengecualian khusus yang mungkin dapat Anda tanggapi dengan cara yang berarti, Anda dapat menangkapnya terlebih dahulu dan melakukannya.
Jika tidak ada dan Anda yakin akan melakukan hal yang sama untuk semua pengecualian dan kesalahan (misalnya keluar dengan pesan kesalahan), daripada menangkap throwable itu tidak masalah.
Biasanya kasing pertama tahan dan Anda tidak akan menangkap lemparan. Tetapi masih ada banyak kasus di mana penangkapan itu berfungsi dengan baik.
Meskipun ini digambarkan sebagai praktik yang sangat buruk, terkadang Anda mungkin menganggapnya langka kasus yang tidak hanya berguna tetapi juga wajib. Berikut dua contoh.
Dalam aplikasi web di mana Anda harus menunjukkan halaman kesalahan penuh arti kepada pengguna. Kode ini memastikan ini terjadi karena ini besar di try/catch
sekitar semua penangan permintaan Anda (servlet, aksi struts, atau pengontrol apa pun ....)
try{
//run the code which handles user request.
}catch(Throwable ex){
LOG.error("Exception was thrown: {}", ex);
//redirect request to a error page.
}
}
Sebagai contoh lain, pertimbangkan Anda memiliki kelas layanan yang melayani bisnis transfer dana. Metode ini mengembalikan TransferReceipt
jika transfer selesai atau NULL
jika tidak bisa.
String FoundtransferService.doTransfer( fundtransferVO);
Sekarang pencitraan Anda mendapatkan List
transfer dana dari pengguna dan Anda harus menggunakan layanan di atas untuk melakukan semuanya.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}
Tetapi apa yang akan terjadi jika ada pengecualian? Anda tidak boleh berhenti, karena satu transfer mungkin berhasil dan satu mungkin tidak, Anda harus terus melanjutkan melalui semua pengguna List
, dan menunjukkan hasil untuk setiap transfer. Jadi Anda berakhir dengan kode ini.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}catch(Throwable ex){
LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
}
}
Anda dapat menjelajahi banyak proyek sumber terbuka untuk melihat throwable
apakah benar-benar di-cache dan ditangani. Misalnya di sini adalah pencarian tomcat
, struts2
dan primefaces
:
https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28 Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable
throwable
, itulah pertanyaan tentang apa ini
Pertanyaannya agak kabur; apakah Anda bertanya "apakah boleh menangkap Throwable
", atau "apakah Throwable
boleh menangkap dan tidak melakukan apa-apa"? Banyak orang di sini menjawab yang terakhir, tapi itu masalah sampingan; 99% dari waktu Anda tidak harus "mengkonsumsi" atau membuang pengecualian, apakah Anda menangkap Throwable
atau IOException
atau apa pun.
Jika Anda menyebarkan pengecualian, jawabannya (seperti jawaban untuk begitu banyak pertanyaan) adalah "tergantung". Itu tergantung pada apa yang Anda lakukan dengan pengecualian — mengapa Anda menangkapnya.
Contoh yang baik mengapa Anda ingin menangkapnya Throwable
adalah dengan menyediakan semacam pembersihan jika ada kesalahan. Misalnya di JDBC, jika terjadi kesalahan selama transaksi, Anda ingin mengembalikan transaksi tersebut:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
Perhatikan bahwa pengecualian tidak dibuang, tetapi disebarkan.
Tetapi sebagai kebijakan umum, menangkap Throwable
karena Anda tidak punya alasan dan terlalu malas untuk melihat pengecualian spesifik mana yang dilemparkan adalah bentuk yang buruk dan ide yang buruk.
Secara umum Anda ingin menghindari penangkapan Error
tetapi saya dapat memikirkan (setidaknya) dua kasus spesifik yang pantas untuk dilakukan:
AssertionError
yang tidak berbahaya.Jika kita menggunakan throwable , maka itu juga mencakup Error dan hanya itu.
Contoh.
public class ExceptionTest {
/**
* @param args
*/
public static void m1() {
int i = 10;
int j = 0;
try {
int k = i / j;
System.out.println(k);
} catch (Throwable th) {
th.printStackTrace();
}
}
public static void main(String[] args) {
m1();
}
}
Keluaran:
java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)
Throwable adalah kelas super dari semua kesalahan dan eksesi. Jika Anda menggunakan Throwable in a catch clause, tidak hanya menangkap semua pengecualian, tetapi juga menangkap semua kesalahan. Kesalahan dilemparkan oleh JVM untuk menunjukkan masalah serius yang tidak dimaksudkan untuk ditangani oleh aplikasi. Contoh umum untuk itu adalah OutOfMemoryError atau StackOverflowError. Keduanya disebabkan oleh situasi yang berada di luar kendali aplikasi dan tidak dapat ditangani. Jadi Anda tidak boleh menangkap Throwable kecuali Anda cukup yakin bahwa itu hanya pengecualian yang berada di dalam Throwable.
Meskipun umumnya praktik yang buruk untuk menangkap Throwable (seperti yang dijelaskan oleh banyak jawaban pada pertanyaan ini), skenario di mana penangkapan Throwable
berguna cukup umum. Izinkan saya menjelaskan satu kasus yang saya gunakan di pekerjaan saya, dengan contoh yang disederhanakan.
Pertimbangkan metode yang melakukan penambahan dua angka, dan setelah penambahan berhasil, metode ini mengirimkan peringatan email ke orang-orang tertentu. Asumsikan bahwa nomor yang dikembalikan penting dan digunakan oleh metode panggilan.
public Integer addNumbers(Integer a, Integer b) {
Integer c = a + b; //This will throw a NullPointerException if either
//a or b are set to a null value by the
//calling method
successfulAdditionAlert(c);
return c;
}
private void successfulAdditionAlert(Integer c) {
try {
//Code here to read configurations and send email alerts.
} catch (Throwable e) {
//Code to log any exception that occurs during email dispatch
}
}
Kode untuk mengirim peringatan email membaca banyak konfigurasi sistem dan karenanya, mungkin ada berbagai pengecualian yang dilemparkan dari blok kode itu. Namun kami tidak ingin pengecualian apa pun yang ditemukan selama pengiriman peringatan diterapkan ke metode pemanggil, karena metode tersebut hanya berkaitan dengan jumlah dari dua nilai Integer yang disediakannya. Oleh karena itu, kode untuk mengirimkan peringatan email ditempatkan dalam satu try-catch
blok, di mana Throwable
tertangkap dan pengecualian apa pun hanya dicatat, memungkinkan sisa aliran untuk melanjutkan.
Exception
dengan segala cara, tapi tidak Throwable
.