Apakah menangkap Throwable merupakan praktik yang buruk?

Jawaban:

104

Anda harus sespesifik mungkin. Jika tidak, serangga yang tidak terduga mungkin akan merayap dengan cara ini.

Selain itu, Throwableselimut Errorjuga dan itu biasanya tidak ada gunanya kembali . Anda tidak ingin menangkap / mengatasinya, Anda ingin program Anda segera mati sehingga Anda dapat memperbaikinya dengan baik.

BalusC
sumber
38
Ada situasi di mana menangkap Kesalahan dan melanjutkan sesuai. Contoh: Dalam servlet, jika Anda melakukan enconter pada OutOfMemoryError karena permintaan tertentu terjadi untuk memakan semua memori, Anda dapat mencoba melanjutkan karena objek akan menjadi GC setelah permintaan ditangani. Hal yang sama berlaku untuk kesalahan pernyataan. Anda tidak mematikan aplikasi karena ada yang tidak beres dalam permintaan.
gawi
7
Bagaimana Anda mengetahui apa yang dialokasikan dan apa yang tidak sebelum OOME? Semua taruhan dibatalkan begitu Anda mendapatkannya, bahkan di dalam wadah J2EE seperti Tomcat atau JBoss.
bmauter
10
Kami memiliki NoSuchMethodError dan untungnya tidak mengambil semua pelanggan kami dengan mematikan server seperti yang terjadi dua minggu setelah penerapan. yaitu. Kami selalu memiliki catchall catching Throwable dan melakukan upaya terbaik untuk menangani dan mengirim kesalahan ke pelanggan. Lagi pula, ada banyak jenis Kesalahan yang dapat dipulihkan yang hanya mempengaruhi 1 dari 1000 pelanggan.
Dean Hiller
10
" Anda ingin program Anda segera mati sehingga Anda dapat memperbaikinya dengan benar " => jika program Anda mati, bagaimana Anda tahu apa yang terjadi? Catching Throwable / Error untuk mencatat masalah adalah hal yang wajar untuk dilakukan ...
assylias
3
@assylias Aplikasi yang berdiri sendiri akan memberikan kesalahan fatal ke stderr.
Philip Whitehouse
36

Ini ide yang buruk. Bahkan, menangkap Exceptionbiasanya 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 ThreadDeathKesalahan. 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);
 }
Brandon Yarbrough
sumber
4
1 untuk menyatakan bahwa menangkap Exception tidak ada gunanya. Setidaknya ada ThreadInterruptedException yang memerlukan perhatian khusus (singkatnya - setelah menangkapnya, Anda harus menyetel status terputus utas kembali ke 'benar')
Kirill Gamazkov
Saya Tahu itu hanya untuk mengilustrasikan kata-kata Anda tetapi saya pikir Anda dapat memeriksa dengan regex apakah input pengguna Anda adalah Alfanumerik atau format apa yang Anda butuhkan dan tidak menggunakan coba tangkap di mana-mana setiap saat.
amdev
Bagaimana saya bisa tahu jika ada Error / Throwable jika saya tidak menangkapnya? Saya tidak melihat apa pun di log. Aplikasi Java EE. Hanya menghabiskan berhari-hari terjebak tidak mengetahui apa masalahnya sampai saya menambahkan tangkapan ini.
Philip Rego
2
Saya menganggap opsi # 2 sebagai praktik yang buruk. Bayangkan 10 panggilan berantai dengan log & rethrow. Jika Anda melihat file log, Anda tidak akan senang. Pengecualian dicatat 10 kali membuat log sangat sulit dibaca. IMHO jauh lebih baik adalah untuk dilakukan throw new Exception("Some additional info, eg. userId " + userId, e);. Ini akan dicatat dalam satu pengecualian bagus dengan 10 penyebab.
Petr Újezdský
21

Perlu diketahui juga bahwa saat menangkap Throwable, Anda juga bisa menangkap InterruptedExceptionyang 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.

gawi
sumber
14

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
Andrew Norman
sumber
13

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.

duffymo
sumber
10
Sangat setuju - ada kasus yang benar-benar sah untuk menangani semua Throwablecontoh - misalnya untuk pencatatan pengecualian khusus.
Yuriy Nakonechnyy
9

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.

DNA
sumber
12
Atau gunakan perpustakaan tertulis yang lebih baik?
Raedwald
6
Memang, jika Anda punya pilihan ;-)
DNA
itulah masalah terbesar dengan menangkap barang yang bisa dibuang dan melemparkannya kembali. itu benar-benar membuat antarmuka yang tidak mungkin untuk semua metode hulu dalam tumpukan. Mereka harus berurusan dengan lemparan yang bisa dilempar atau memiliki tanda tangan lemparan yang tidak dapat digunakan yang harus ditangani orang lain.
Andrew Norman
6

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.

ic3
sumber
5

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.

b. kepemilikan
sumber
4

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/catchsekitar 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 TransferReceiptjika transfer selesai atau NULLjika tidak bisa.

String FoundtransferService.doTransfer( fundtransferVO);

Sekarang pencitraan Anda mendapatkan Listtransfer 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 throwableapakah benar-benar di-cache dan ditangani. Misalnya di sini adalah pencarian tomcat, struts2dan 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

Alireza Fattahi
sumber
1
Lihat kode di tautan ini. Yang bisa dilempar bukan hanya yang ditangkap! Ada pengecualian lain yang ditangkap juga sebelum Throwable.
developer1011
@ developer101 tentu saja, tetapi mereka menangkap throwable, itulah pertanyaan tentang apa ini
Alireza Fattahi
4

Pertanyaannya agak kabur; apakah Anda bertanya "apakah boleh menangkap Throwable", atau "apakah Throwableboleh 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 Throwableatau IOExceptionatau 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 Throwableadalah 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 Throwablekarena Anda tidak punya alasan dan terlalu malas untuk melihat pengecualian spesifik mana yang dilemparkan adalah bentuk yang buruk dan ide yang buruk.

Garret Wilson
sumber
1

Secara umum Anda ingin menghindari penangkapan Errortetapi saya dapat memikirkan (setidaknya) dua kasus spesifik yang pantas untuk dilakukan:

  • Anda ingin menutup aplikasi sebagai tanggapan atas kesalahan, khususnya AssertionError yang tidak berbahaya.
  • Apakah Anda mengimplementasikan mekanisme penggabungan thread yang mirip dengan ExecutorService.submit () yang mengharuskan Anda meneruskan pengecualian kembali ke pengguna sehingga mereka bisa menanganinya.
Gili
sumber
0

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)
VicXj
sumber
0

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.

Sidath Bhanuka Randeniya
sumber
-1

Meskipun umumnya praktik yang buruk untuk menangkap Throwable (seperti yang dijelaskan oleh banyak jawaban pada pertanyaan ini), skenario di mana penangkapan Throwableberguna 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-catchblok, di mana Throwabletertangkap dan pengecualian apa pun hanya dicatat, memungkinkan sisa aliran untuk melanjutkan.

CodeNewbie
sumber
Saya akan mencoba dan menghindari ini dengan memiliki utas yang didedikasikan untuk pekerjaan (dengan antrian) mengirim email.
boumbh
Saya menyarankan bahwa ini masih kode yang buruk. Tangkap Exceptiondengan segala cara, tapi tidak Throwable.
andrewf