Throwable'ı yakalamak kötü bir uygulama mı?


Yanıtlar:


104

Mümkün olduğunca spesifik olmanız gerekir. Aksi takdirde öngörülemeyen böcekler bu şekilde uzaklaşabilir.

Ayrıca, Throwablekapaklar Errorda ve bu genellikle geri dönüş noktası değildir . Bunu yakalamak / halletmek istemezsiniz, programınızın derhal ölmesini istersiniz, böylece onu düzgün bir şekilde düzeltebilirsiniz.


38
Hata yakalayıp devam etmenin uygun olduğu durumlar vardır. Örn: Sunucu uygulamasında, belirli bir istek tüm belleği yediği için bir OutOfMemoryError kodlarsanız, istek işlendikten sonra nesneler GC olacağı için devam etmeyi deneyebilirsiniz. Aynı şey bir iddia hatası için de geçerli. İstekte bir şeyler ters gittiği için bir uygulamayı kapatmazsınız.
gawi

7
OOME öncesinde neyin tahsis edildiğini ve neyin olmadığını nasıl anlarsınız? Tomcat veya JBoss gibi bir J2EE konteyneri içinde bile, bunu aldığınızda tüm bahisler kapanır.
bmauter

10
NoSuchMethodError'a sahip olduk ve şükür ki, dağıtımdan iki hafta sonra olduğu gibi sunucuyu kapatarak tüm müşterilerimizi dışarı çıkarmadık. yani. Her zaman, Throwable'ı yakalayan ve müşteriye hatayı işlemek ve müşteriye göndermek için en iyi çabayı gösteren bir yakalama var. Sonuçta, kurtarılabilen birçok Hata türü vardır, çünkü yalnızca 1000 müşteriden 1'ini etkileyebilir.
Dean Hiller

10
" programınızın derhal ölmesini istiyorsanız, böylece onu düzgün bir şekilde düzeltebilirsiniz " => programınız ölürse, ne olduğunu nasıl anlarsınız? Atılabilir / Hatayı günlüğe kaydetmek için yakalamak mantıklı bir şey ...
assylias

3
@assylias Bağımsız bir uygulama stderr'e ölümcül bir hata atar.
Philip Whitehouse

36

Bu kötü bir fikir. Aslında, yakalamak bile Exceptiongenellikle kötü bir fikirdir. Bir örnek ele alalım:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

Şimdi, getUserInput () işlevinin bir süre engellendiğini ve başka bir iş parçacığının iş parçacığınızı mümkün olan en kötü şekilde durdurduğunu varsayalım (thread.stop () çağırır). Yakalama bloğunuz bir ThreadDeathHata yakalayacaktır . Bu çok kötü. Bu İstisnayı yakaladıktan sonra kodunuzun davranışı büyük ölçüde tanımsızdır.

İstisna yakalanırken de benzer bir sorun oluşur. Belki getUserInput()bir InterruptException nedeniyle başarısız olabilir veya sonuçları günlüğe kaydetmeye çalışırken bir izin reddedilen istisna veya her türlü başka hata olabilir. Neyin yanlış gittiğine dair hiçbir fikriniz yok, çünkü bu yüzden sorunu nasıl çözeceğiniz konusunda da hiçbir fikriniz yok.

Üç daha iyi seçeneğiniz var:

1 - Nasıl işleneceğini bildiğiniz İstisnaları tam olarak yakalayın:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2 - Karşılaştığınız istisnaları yeniden atın ve nasıl başa çıkacağınızı bilmiyorsanız:

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3 - Son olarak bir blok kullanın, böylece yeniden atmayı hatırlamak zorunda kalmazsınız:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }

4
İstisna'yı yakalamanın bile iyi olmadığını belirten +1. En azından özel bakım gerektiren bir ThreadInterruptedException vardır (kısaca - o yakalandıktan sonra, sen 'gerçek' olarak ayarlanan parçacığının kesintiye durum arkasına gerekir)
Kirill Gamazkov

Bunun sadece kelimelerinizi göstermek için olduğunu biliyorum, ancak kullanıcı girişinizin Alfasayısal olup olmadığını veya hangi formata ihtiyacınız olduğunu bir normal ifadeyle kontrol edebilirsiniz ve her zaman her yerde deneyin.
amdev

Yakalamazsam bir Hata / Atılabilir olup olmadığını nasıl bilebilirim? Günlüklerde hiçbir şey görmedim. Java EE uygulaması. Bu yakalamayı ekleyene kadar sorunun ne olduğunu bilmeden sıkışmış günler geçirdim.
Philip Rego

2
2. seçeneği kötü bir uygulama olarak görüyorum. Günlüğe kaydetme ve yeniden atma ile 10 zincirlenmiş aramayı hayal edin. Günlük dosyasına bakarsanız mutlu olmayacaksınız. İstisna, 10 kez kaydedilir ve günlüklerin okunmasını çok zorlaştırır. IMHO çok daha iyi yapmaktır throw new Exception("Some additional info, eg. userId " + userId, e);. Bu, 10 nedenden oluşan güzel bir istisna ile kaydedilir.
Petr Újezdský

21

Ayrıca Throwable, yakaladığınız zaman , InterruptedExceptionözel bir tedavi gerektiren bir şeyi yakalayabileceğinizi de unutmayın . Daha fazla ayrıntı için InterruptedException ile Başa Çıkma konusuna bakın .

Yalnızca kontrol edilmeyen istisnaları yakalamak istiyorsanız, bu modeli de düşünebilirsiniz.

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

Bu şekilde, kodunuzu değiştirdiğinizde ve kontrol edilen bir istisna atabilecek bir yöntem çağrısı eklediğinizde, derleyici size bunu hatırlatır ve bu durumda ne yapacağınıza karar verebilirsiniz.


14

doğrudan Error sınıfının javadoc'undan (bunların yakalanmamasını önerir):

 * 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

13

Bir yöntemden kesinlikle bir istisna baloncuğuna sahip olamazsanız, bu kötü bir uygulama değildir.

İstisnayı gerçekten kaldıramazsanız, bu kötü bir uygulamadır. Yöntem imzasına "fırlatmalar" eklemek, sadece yakala ve yeniden fırlatmaktan veya daha kötüsü, onu bir RuntimeException içine sarmak ve yeniden atmaktan daha iyidir.


10
Tamamen katılıyorum - tüm Throwableörneklerin işlenmesi için kesinlikle yasal durumlar vardır - örneğin özel istisna günlüğü için.
Yuriy Nakonechnyy

9

Aşırı hevesle Hata veren kitaplıklar kullanıyorsanız, Throwable'ı yakalamak bazen gereklidir, aksi takdirde kitaplığınız uygulamanızı öldürebilir.

Bununla birlikte, bu koşullar altında en iyisi, tüm Fırlatılabilirler yerine yalnızca kitaplık tarafından atılan belirli hataları belirtmek olacaktır.


12
Veya daha iyi yazılmış bir kitaplık mı kullanıyorsunuz?
Raedwald

6
Gerçekten, seçeneğin varsa ;-)
DNA

atılabilir eşyaları yakalamak ve yeniden fırlatmakla ilgili en büyük sorun budur. yığındaki tüm yöntemler için gerçekten imkansız bir arayüz oluşturur. Ya atılabilir bir şeyle uğraşmak zorundalar ya da başkalarının uğraşmak zorunda kalacağı kullanılamaz atılabilir bir imzaya sahipler.
Andrew Norman

6

Fırlatılabilir, tüm sınıflar için atılabilecek temel sınıftır (yalnızca istisnalar değil). Bir OutOfMemoryError veya KernelError yakalarsanız yapabileceğiniz çok az şey var (bkz. Java.lang.Error ne zaman yakalanır? )

İstisnaları yakalamak yeterli olmalıdır.


5

mantığınıza bağlıdır veya seçeneklerinize / olasılıklarınıza daha spesifiktir. Anlamlı bir şekilde tepki verebileceğiniz herhangi bir özel istisna varsa, önce onu yakalayıp bunu yapabilirsiniz.

Eğer yoksa ve tüm istisnalar ve hatalar için aynı şeyi yapacağınızdan eminseniz (örneğin bir hata mesajı ile çıkış), atılabilir olanı yakalamak sorun olmaz.

Genellikle ilk vaka geçerlidir ve fırlatılabilir olanı yakalayamazsınız. Ancak yine de yakalamanın iyi çalıştığı birçok durum var.


4

Çok kötü bir uygulama olarak tanımlansa da, bazen sadece yararlı değil aynı zamanda zorunlu olduğu nadir durumlar da bulabilirsiniz . İşte iki örnek.

Kullanıcıya tam anlamıyla bir hata sayfası göstermeniz gereken bir web uygulamasında. Bu kod, try/catchtüm istek işleyicileriniz (servletler, struts eylemleri veya herhangi bir denetleyici ....) etrafında büyük olduğu için bunun olmasını sağlar.

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

Başka bir örnek olarak, fon transferi işine hizmet eden bir hizmet sınıfınız olduğunu düşünün. Bu yöntem TransferReceipt, transfer yapılırsa veya yapılamazsa bir döndürür NULL.

String FoundtransferService.doTransfer( fundtransferVO);

Şimdi List, kullanıcıdan bir para transferi alıyorsunuz ve hepsini yapmak için yukarıdaki hizmeti kullanmanız gerekiyor.

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

Ama herhangi bir istisna olursa ne olacak ? Durmamalısınız, çünkü bir transfer başarılı olmuş olabilir ve biri olmayabilir, tüm kullanıcılar üzerinden devam etmeli Listve her transfer için sonucu göstermelisiniz. Yani bu kodla sonuçlanırsınız.

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

throwableGerçekten önbelleğe alındığını ve işlendiğini görmek için birçok açık kaynaklı projeye göz atabilirsiniz . Örneğin burada tomcat, struts2ve 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 % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable


1
Kodu bu bağlantılarda gördüm. Fırlatılabilir sadece yakalanan değil! Throwable'dan önce yakalanan başka istisnalar da var.
developer1011

@ developer101 elbette, ama yakaladılar throwable, bu soru da ne hakkında
Alireza Fattahi

4

Soru biraz belirsiz; "Yakalamak Throwablesorun olur Throwablemu" mu , yoksa "a'yı yakalayıp hiçbir şey yapmamak sorun olur mu?" Buradaki birçok kişi ikincisini yanıtladı, ama bu bir yan mesele; Yakalama Throwableya IOExceptionda her neyse , zamanın% 99'u istisnayı "tüketmemeli" veya atmamalısınız .

İstisnayı yayarsanız, cevap (pek çok sorunun cevabı gibi) "duruma göre değişir". Bu istisna dışında ne yaptığınıza, neden yakaladığınıza bağlıdır.

Neden yakalamak isteyeceğinize iyi bir örnek Throwable, herhangi bir hata varsa bir tür temizleme sağlamaktır. Örneğin, JDBC'de, bir işlem sırasında bir hata meydana gelirse, işlemi geri almak isteyebilirsiniz:

try {
  
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

İstisnanın atılmadığını, ancak yayıldığını unutmayın.

Ancak genel bir politika olarak, Throwablebir sebebiniz olmadığı ve hangi özel istisnaların atıldığını göremeyecek kadar tembel olduğunuz için yakalamak kötü bir biçim ve kötü bir fikirdir.


1

Genel olarak, e-postaları yakalamaktan kaçınmak istersiniz, Errorancak (en azından) bunu yapmanın uygun olduğu iki özel durum düşünebilirim:

  • Özellikle AssertionError zararsız olan hatalara yanıt olarak uygulamayı kapatmak istiyorsunuz .
  • ExecutorService.submit () 'e benzer bir iş parçacığı havuzlama mekanizması mı uyguluyorsunuz ?

0

Kullandığımız takdirde throwable , o zaman kapsar Hata sıra ve bu kadar.

Misal.

    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();
}

}

Çıktı:

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)

0

Fırlatılabilir, tüm hataların ve dışlamaların üst sınıfıdır. Throwable'ı bir catch cümlesinde kullanırsanız, yalnızca tüm istisnaları değil, tüm hataları da yakalayacaktır. Bir uygulama tarafından ele alınması amaçlanmayan ciddi sorunları belirtmek için JVM tarafından hatalar atılır. Bunun tipik örnekleri OutOfMemoryError veya StackOverflowError'dır. Her ikisi de uygulamanın kontrolü dışında olan ve ele alınamayan durumlardan kaynaklanır. Bu nedenle, bunun yalnızca Throwable içinde bir istisna olacağından oldukça emin değilseniz, Throwables'ı yakalamamalısınız.


-1

Throwable'ı yakalamak genellikle kötü bir uygulama olsa da (bu soruya verilen sayısız yanıtla açıklandığı üzere), yakalamanın Throwableyararlı olduğu senaryolar oldukça yaygındır. İşimde kullandığım böyle bir durumu basitleştirilmiş bir örnekle açıklayayım.

İki sayının eklenmesini gerçekleştiren bir yöntem düşünün ve başarılı bir şekilde eklendikten sonra, belirli kişilere bir e-posta uyarısı gönderir. Döndürülen sayının önemli olduğunu ve arama yöntemi tarafından kullanıldığını varsayın.

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
    }
}

E-posta uyarıları göndermek için kullanılan kod, birçok sistem yapılandırmasını okur ve bu nedenle, bu kod bloğundan atılan çeşitli istisnalar olabilir. Ancak, uyarı gönderimi sırasında karşılaşılan herhangi bir istisnanın çağıran yönteme yayılmasını istemiyoruz, çünkü bu yöntem yalnızca sağladığı iki Tamsayı değerinin toplamıyla ilgilidir. Bu nedenle, e-posta uyarılarını gönderecek kod , yakalandığı ve istisnaların yalnızca günlüğe kaydedildiği bir try-catchbloğa yerleştirilir Throwableve akışın geri kalanının devam etmesine izin verir.


E-posta gönderme işine (kuyruklu) adanmış bir iş parçacığına sahip olarak bundan kaçınmaya çalışırdım.
boumbh

Bunun hala kötü bir kod olduğunu öneririm. Elbette yakala Exception, ama değil Throwable.
andrewf
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.