Java istisnası yakalanmadı mı?


170

Try-catch yapıları ile ilgili küçük bir teorik sorunum var.

Dün Java ile ilgili pratik bir sınava girdim ve aşağıdaki örneği anlamıyorum:

try {
    try {
        System.out.print("A");
        throw new Exception("1");
    } catch (Exception e) {
        System.out.print("B");
        throw new Exception("2");
    } finally {
        System.out.print("C");
        throw new Exception("3");
    }
} catch (Exception e) {
    System.out.print(e.getMessage());
}

Soru "çıktı nasıl görünecekti?"

AB2C3 olacağından emindim, ama sürpriz sürpriz, bu doğru değil.

Doğru cevap ABC3 (test edildi ve gerçekten böyle).

Sorum şu, İstisna ("2") nereye gitti?


8
+1 Ahh, bu cevabı biliyordum. Bunu bir röportajda istedim. Deneme / yakalama / nihayet yığın üzerinde nasıl çalıştığını anlamak için çok iyi bir soru.
Ama Ben

10
Bir sayı yazdırabilecek yalnızca bir print bildirimi vardır (son:) print(e.getMessage()). Çıktının olacağını düşündünüz AB2C3: en dıştaki catchbloğun iki kez yürütülebileceğini mi düşünüyordunuz ?
Adrian Pronk

Java'da, yakalama bloğundan aktarım denetimini yürüten bir komut yürütülmeden önce, son blok varsa varolan yürütülür. Yalnızca son bloktaki kod kontrolü dışarıya aktarmazsa, yakalama bloğundan geciken talimat yürütülür.
Thomas

Yanıtlar:


198

Gönderen Java Dil Şartname 14.20.2. :

Yakalama bloğu R nedeni nedeniyle aniden tamamlanırsa, son blok yürütülür. Sonra bir seçenek var:

  • Nihayet blok normal şekilde tamamlanırsa, try ifadesi R nedeni için aniden tamamlanır.

  • Sonunda blok S nedeni için aniden tamamlanırsa, try ifadesi S nedeni için aniden tamamlanır (ve R nedeni atılır) .

Yani, bir istisna atan bir catch bloğu olduğunda:

try {
    // ...
} catch (Exception e) {
    throw new Exception("2");
}

ama aynı zamanda bir istisna getiren bir de son blok var:

} finally {
    throw new Exception("3");
}

Exception("2")atılır ve yalnızca Exception("3")yayılır.


72
Bu returnifadeler için bile geçerlidir . Sonunda bloğunuzun bir dönüşü varsa, bir tryveya catchbloktaki herhangi bir dönüşü geçersiz kılar . Bu "özellikler" nedeniyle, iyi bir uygulama, nihayet bloğun asla bir istisna atmaması veya bir iade ifadesi olmamasıdır .
Augusto

Bu, Java 7'de kaynaklarla deneme özelliğinin miras avantajıdır. Kaynakları kapatırken ikincil bir kural dışı durum oluşturulduğunda ilk istisnayı korur ve bu da hata ayıklamayı kolaylaştırır.
w25r

19

Sonunda blokta atılan istisnalar, daha önce try veya catch bloğunda atılan istisnayı bastırır.

Java 7 örneği: http://ideone.com/0YdeZo

Gönderen JavaDoc'u en Örneğin:


static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

Ancak bu örnekte, readLine ve close yöntemlerinin her ikisi de istisnalar atarsa, readFirstLineFromFileWithFinallyBlock yöntemi, nihayet bloğundan atılan istisnayı atar; try bloğundan atılan istisna bastırılır.


try-withJava 7'nin yeni sözdizimi başka bir istisna bastırma adımı ekler: Deneme bloğunda atılan istisnalar, deneme bölümünde daha önce atılanları bastırır.

aynı örnekten:

try (
        java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
        java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
            String newLine = System.getProperty("line.separator");
            String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
            writer.write(zipEntryName, 0, zipEntryName.length());
        }
    }

Kaynaklarla dene deyimi ile ilişkili kod bloğundan bir istisna atılabilir. Yukarıdaki örnekte, try bloğundan bir istisna atılabilir ve ZipFile ve BufferedWriter nesnelerini kapatmaya çalıştığında, try-with-resources deyiminden en fazla iki istisna atılabilir. Try bloğundan bir istisna atılırsa ve kaynakları dene denemesinden bir veya daha fazla istisna atılırsa, try-with-resources ifadesinden atılan istisnalar bastırılır ve blok tarafından atılan istisna writeToFileZipFileContents yöntemi tarafından atılır. Bu engellenmiş istisnaları, try bloğu tarafından atılan istisnadan Throwable.getSuppressed yöntemini çağırarak alabilirsiniz.


Söz konusu kodda, her blok eski istisnayı açıkça atar, hatta günlüğe kaydetmez, bazı hataları çözmeye çalışırken iyi değildir:

http://en.wikipedia.org/wiki/Error_hiding


9

Yana throw new Exception("2");atılır catchbloğu olup try, tekrar yakalanmış edilmeyecektir.
Bkz. 14.20.2. Sonunda dene-yakala ve sonunda yakala-yakala .

Olan şey budur:

try {
    try {
        System.out.print("A");         //Prints A
        throw new Exception("1");   
    } catch (Exception e) { 
        System.out.print("B");         //Caught from inner try, prints B
        throw new Exception("2");   
    } finally {
        System.out.print("C");         //Prints C (finally is always executed)
        throw new Exception("3");  
    }
} catch (Exception e) {
    System.out.print(e.getMessage());  //Prints 3 since see (very detailed) link
}

evet bu doğru, bunun olduğunu görüyorum, ama ben açıklama yapıyordum - neden bu şekilde davranıyor
Kousalik

5

Sorunuz çok açık ve yanıtı aynı ölçüde basittir. "2" iletisine sahip Exception nesnesinin üzerine "3" iletisini içeren Exception nesnesinin üzerine yazılır.

Açıklama: Bir İstisna meydana geldiğinde, nesnesi işlemek için blok yakalamak için atılır. Ancak catch bloğunda istisna oluştuğunda, nesnesi istisna İşleme için OUTER CATCH Bloğuna (varsa) aktarılır. Ve aynısı burada oldu. "2" iletisine sahip İstisna Nesnesi, OUT catch bloğuna aktarılır. Ama bekle .. İç try-catch bloğundan ayrılmadan önce SONA YÜRÜTÜLMELİDİR. İşte endişe duyduğumuz değişiklik oldu. Yeni bir EXCEPTION nesnesi ("3" iletisiyle) atılır veya zaten atılan Exception nesnesinin yerini alan ("2" iletisiyle) bu blok engellenir, bunun sonucunda, Exception nesnesi iletisi yazdırıldığında geçersiz kılınan değer, yani "2" değil, "3".

Unutmayın: CATCH bloğunda yalnızca bir istisna nesnesi işlenebilir.


2

finallyBlok hep çalışır. Ya returntry bloğunun içinden ya da bir istisna atılır. finallyBlokta atılan istisna , yakalama dalında atılan istisnayı geçersiz kılar.

Ayrıca, bir istisna atmak tek başına herhangi bir çıktıya neden olmaz. Hat throw new Exception("2");hiçbir şey yazmaz.


1
Evet, Exception çıktısını tek başına atmayı biliyorum, ama neden görmedim, Exception 2'nin neden bırakılması gerektiğini. Yine biraz daha
zekiyim

her zaman çok uzun zaman ve çok uzun zaman içinde bir şey olabilir (bulmaca wouter.coekaerts.be/2012/puzzle-dreams kontrol edin )
Dainius

0

Kodunuza göre:

try {
    try {
        System.out.print("A");
        throw new Exception("1");   // 1
    } catch (Exception e) {
        System.out.print("B");      // 2
        throw new Exception("2");
    } finally {                     // 3
        System.out.print("C");      // 4 
        throw new Exception("3");
    }
} catch (Exception e) {             // 5
    System.out.print(e.getMessage());
}

Burada görebileceğiniz gibi:

  1. A'yı yazdırır ve istisna atar # 1;
  2. bu istisna catch deyimi ve baskı ile yakalanmıştır B - # 2;
  3. block nihayet # 3try-catch (ya da herhangi bir istisna olmamışsa dene) ifadesinden sonra yürütür C - # 4ve yeni istisna yazdırır ;
  4. bu harici catch deyimi tarafından yakalandı # 5;

Sonuç ABC3. Ve 2aynı şekilde çıkarılır1


Üzgünüz, İstisna ("1") atlanmadı, ancak başarıyla yakalandı
Black Maggie

@Siyah Maggie Önbellekte saklanır ve yeni bir istisna atılır => bu önbelleğe alınmaz ve program sonlandırılır. Ve bu blok önce nihayet yürütülür.
nazar_art
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.