Yakalama bloğunun içine atılan istisna - tekrar yakalanacak mı?


180

Bu bir programlama 101 sorusu gibi görünebilir ve cevabı bildiğimi sanıyordum ama şimdi kendimi tekrar kontrol etmem gerekiyor. Aşağıdaki kodda, ilk catch bloğuna atılan istisna daha sonra aşağıdaki genel Exception catch bloğu tarafından yakalanacak mı?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

Her zaman cevabın hayır olacağını düşündüm, ama şimdi bunun neden olabileceği garip bir davranışım var. Cevap muhtemelen çoğu dil için aynıdır ancak Java ile çalışıyorum.


2
Belki de "garip davranışı" tarif edebilirsin?
Jeffrey L Whitledge

ApplicationException'ın başka bir yere atılmadığından ve bu bloğa yayılmadığından emin misiniz?
sblundy

Bunu Eclipse IDE'mde fark ettim. Beni "yeni istisna fırlat" ı bir deneme bloğuna koymaya zorluyor ama nedenini bilmiyorum. Bunu geçmişte yapmadan yaptım. Deneme bloğunun neden gerekli olduğunu anlamıyorum. Google'daki birçok örnek, kullanıcıların bir deneme engellemesine ihtiyaç duymadığını gösterir. Bir if ifadesinin içine attığım için mi?
djangofan

Yanıtlar:


214

Hayır, çünkü yeni doğrudan blokta throwdeğil try.


Kodun böyle olup olmadığını söyleyelim {} catch (Exception e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("In catch IOException:" + e.getClass ()); } ve try bloğundaki kod IO Exception oluşturur, Hemen genel Exception bloğuna gider mi yoksa IOException catch bloğuna uçar mı?
sofs1

4
@ user3705478 Bu tür hatalı durumlardan kaçınmak için kod derlenmeyecektir. Genel olarak, aynı deneme bloğunda üst sınıfını yakaladıktan sonra bir alt sınıf için yakalamanıza izin verilmez.
Chris Jester-Young

Teşekkürler. Derleme zamanı hatası alıyorum, değil mi? Eve geldiğimde test edeceğim.
sofs1

@ User3705478'e bağlıdır, eğer bloktan a RuntimeExceptionatılırsa catchderleme hatası olmaz.
Randy the Dev

@AndrewDunn user3705478'in sorusunun nedeninin bu olduğunu düşünmüyorum, daha ziyade, bir alt kural dışı durum yan tümcesinden önce bir üst kural dışı durum catchyan tümcesi listelenirse ne olur . Anladığım kadarıyla Java buna izin vermiyor ve derleme zamanında yakalanıyor. catch
Chris Jester-Young

71

Hayýr. Kontrol etmek çok kolay.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

Yazdırmalı:

Catch IOException: sınıf java.io.IOException
Sonunda
"Main" iş parçacığında özel durum java.lang.RuntimeException
        Catch.main'de (Catch.java:8)

Teknik olarak bu bir derleyici hatası, uygulamaya bağlı, belirtilmemiş davranış veya başka bir şey olabilir. Ancak, JLS oldukça iyi çivilenmiş ve derleyiciler bu tür basit şeyler için yeterince iyi (jenerikler köşe çantası farklı bir sorun olabilir).

Ayrıca, iki catch bloğu etrafında takas yaparsanız, derlemeyeceğinizi unutmayın. İkinci yakalama tamamen ulaşılamaz.

Nihayet bloğun bir catch bloğu yürütülmüş olsa bile her zaman çalıştığına dikkat edin (sonsuz döngüler, araçlar arayüzünden takma ve iş parçacığını öldürme, bayt kodunu yeniden yazma vb.).


3
A'dan kaçınmanın en belirgin yolu finallyelbette aramaktır System.exit. :-P
Chris Jester-Young

3
@Chris Jester-Young for(;;);daha kısadır, dilde bulunur, yan etkiler yolunda fazla bir şey yapmaz ve benim için daha açıktır.
Tom Hawtin - tackline

1
System.exitCPU'nun dostudur! : -Ama evet, tamam, açıkça bu öznel bir kriter. Ayrıca, bir kod golfçüsü olduğunu bilmiyordum. ;-)
Chris Jester-Young

28

Java Dil Spesifikasyonu bölüm 14.19.1'de diyor:

Deneme bloğunun yürütülmesi, V değerinin atılması nedeniyle aniden tamamlanırsa, bir seçenek vardır:

  • Çalışma zamanı türü V, try ifadesinin herhangi bir catch yan tümcesinin Parametresine atanabilirse, ilk (en soldaki) bu catch yan tümcesi seçilir. V değeri, seçilen catch deyiminin parametresine atanır ve bu catch deyiminin Bloğu yürütülür. Bu blok normal şekilde tamamlanırsa, try ifadesi normal şekilde tamamlanır; bu blok herhangi bir nedenle aniden tamamlanırsa, try ifadesi aynı nedenden dolayı aniden tamamlanır.

Referans: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

Başka bir deyişle, istisnayı işleyebilen ilk çevreleyici yakalama yapar ve bu yakalamadan bir istisna atılırsa, bu, orijinal deneme için başka bir yakalamanın kapsamı içinde değildir, bu yüzden onu ele almaya çalışmazlar.

Bilmekle ilgili ve kafa karıştırıcı bir şey, bir try- [catch] -finally yapısında, nihayet bir bloğun bir istisna atabileceği ve eğer öyleyse, try veya catch bloğu tarafından atılan herhangi bir istisnanın kaybolacağıdır. İlk gördüğünüzde kafa karıştırıcı olabilir.


Sadece Java 7'den beri try-with-resources kullanmaktan kaçınabileceğinizi eklemek istedim . Daha sonra, tryAND finallyher ikisi de atılırsa, finallybastırılır, ancak istisna dışında EKLENDİRİLİR try. Eğer catchhem atıyor sen 'addSuppressed' yoluyla kendinizi idare ve ekleme sürece, hiçbir şanslısınız tryistisna - o zaman üçünü de var.
LAFK, Reinstate Monica'ya

6

Catch bloğundan bir istisna atmak istiyorsanız, yönteminizi / sınıfınızı / vb. söz konusu istisnayı atması gerektiği. Şöyle ki:

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

Ve şimdi derleyiciniz size bağırmayacak :)


4

Hayır - Chris Jester-Young'ın dediği gibi, hiyerarşideki bir sonraki deneme yakalamaya atılacak.


2

Yukarıda belirtildiği gibi ...
Neler olup bittiğini görmekte sorun yaşıyorsanız, sorunu hata ayıklayıcıda yeniden oluşturamazsanız, yeni istisnayı yeniden atmadan önce bir iz ekleyebilirsiniz (iyi eski Sistem ile) .out.println daha da kötüsü, aksi takdirde log4j gibi iyi bir günlük sistemi ile).


2

İkinci yakalama bloğu tarafından yakalanmaz. Her İstisna yalnızca bir try bloğunun içindeyken yakalanır. Deneme iç içe yerleştirebilirsiniz (genellikle iyi bir fikir değil):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}

Benzer bir kod yazdım. Ama ikna olmadım. Yakalama bloğumda bir Thread.sleep () çağırmak istiyorum. Ancak Thread.sleep, bir InterruptedException kurar. Örneğinizde gösterdiğiniz gibi yapmak doğru mu (en iyi uygulama)?
riroo

1

Hayır, yakalamaların tümü aynı deneme bloğuna atıfta bulunduğundan, yakalama bloğunun içinden atmak, kapalı bir deneme bloğu tarafından yakalanır (muhtemelen bunu deneyen yöntemde)


-4

Eski yazı ancak "e" değişkeni benzersiz olmalıdır:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

5
Bu bir yorum olmalı, cevap değil. Asıl soruyu ele alma yolunda hiçbir şey sağlamaz - bu sadece OP sorununun eleştirisidir.
Derek W
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.