Pengecualian dimasukkan ke dalam blok penangkap - apakah akan ditangkap lagi?

180

Ini mungkin tampak seperti pertanyaan pemrograman 101 dan saya pikir saya tahu jawabannya tetapi sekarang saya perlu memeriksa ulang. Dalam potongan kode di bawah ini, akankah pengecualian yang dilemparkan ke dalam blok tangkapan pertama kemudian ditangkap oleh blok tangkapan umum pengecualian di bawah ini?

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

Saya selalu berpikir jawabannya adalah tidak, tetapi sekarang saya memiliki beberapa perilaku aneh yang bisa disebabkan oleh ini. Jawabannya mungkin sama untuk sebagian besar bahasa, tetapi saya bekerja di Jawa.

roryf
sumber
2
Mungkin Anda bisa menggambarkan "perilaku aneh"?
Jeffrey L Whitledge
Apakah Anda yakin ApplicationException tidak dilempar ke tempat lain dan menyebar ke blok ini?
sblundy
Saya perhatikan ini di Eclipse IDE saya. Itu memaksa saya untuk menempatkan "lempar Pengecualian baru" di blok percobaan tetapi saya tidak tahu mengapa. Saya telah melakukannya di masa lalu tanpa melakukan itu. Saya tidak melihat mengapa blok percobaan diperlukan. Banyak contoh di Google menunjukkan orang-orang tidak memerlukan blok percobaan. Apakah itu karena saya memasukkan pernyataan if?
djangofan

Jawaban:

214

Tidak, karena yang baru throwtidak ada di tryblok secara langsung.

Chris Jester-Young
sumber
Katakanlah jika kodenya seperti ini coba {} catch (Exception e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("In catch IOException:" + e.getClass ()); } dan kode di blok percobaan menghasilkan Pengecualian IO, Apakah akan langsung menuju ke blok Pengecualian umum langsung atau akan terbang ke blok tangkap IOException?
sofs1
4
@ user3705478 Kode tidak akan dikompilasi, untuk menghindari situasi yang salah semacam itu. Secara umum, Anda tidak diizinkan memiliki tangkapan untuk subkelas setelah menangkap superclass di blok percobaan yang sama.
Chris Jester-Young
Terima kasih. Jadi saya akan mendapatkan kesalahan waktu kompilasi, bukan? Saya akan mengujinya ketika sampai di rumah.
sofs1
Itu tergantung @ user3705478, jika RuntimeExceptiondilempar dari catchblok tidak akan ada kesalahan kompilasi.
Randy the Dev
@AndrewDunn Saya tidak berpikir itu tentang pertanyaan user3705478 tentang, tetapi, apa yang terjadi jika catchklausa pengecualian orangtua terdaftar sebelumcatch klausa pengecualian anak . Pemahaman saya adalah bahwa Java tidak mengizinkan ini, dan itu ditangkap pada waktu kompilasi.
Chris Jester-Young
71

Tidak. Sangat mudah untuk diperiksa.

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

Harus mencetak:

Dalam menangkap IOException: kelas java.io.IOException
Akhirnya
Pengecualian di utas "utama" java.lang.RuntimeException
        di Catch.main (Catch.java:8)

Secara teknis itu bisa jadi bug penyusun, implementasi tergantung, perilaku tidak ditentukan, atau sesuatu. Namun, JLS sudah dipakukan dengan sangat baik dan kompilernya cukup bagus untuk hal yang sederhana ini (case sudut generik mungkin masalah yang berbeda).

Juga perhatikan, jika Anda bertukar sekitar dua blok tangkap, itu tidak akan dikompilasi. Tangkapan kedua akan benar-benar terjangkau.

Perhatikan bahwa akhirnya blok selalu berjalan bahkan jika blok tangkapan dieksekusi (selain dari kasus konyol, seperti loop tak terbatas, melampirkan melalui antarmuka alat dan membunuh utas, menulis ulang bytecode, dll.).

Tom Hawtin - tackline
sumber
3
Cara yang paling jelas untuk menghindari a finally, tentu saja, untuk menelepon System.exit. :-P
Chris Jester-Young
3
@ Chris Jester-Young for(;;);lebih pendek, terkandung dalam bahasa, tidak memperkenalkan banyak cara efek samping dan, bagi saya, lebih jelas.
Tom Hawtin - tackline
1
System.exitlebih ramah dengan CPU! : -O Tapi ya, oke, jelas ini adalah kriteria subjektif. Juga, saya tidak tahu Anda menjadi pegolf kode. ;-)
Chris Jester-Young
28

Spesifikasi Bahasa Jawa mengatakan di bagian 14.19.1:

Jika eksekusi blok uji selesai tiba-tiba karena lemparan nilai V, maka ada pilihan:

  • Jika tipe run-time dari V ditetapkan untuk Parameter dari setiap klausa catch dari pernyataan try, maka klausa catch yang pertama (paling kiri) dipilih. Nilai V ditetapkan ke parameter klausa tangkapan yang dipilih, dan Blok klausa tangkapan tersebut dijalankan. Jika blok itu selesai secara normal, maka pernyataan coba selesai secara normal; jika blok itu tiba-tiba selesai karena alasan apa pun, maka pernyataan coba selesai dengan alasan yang sama.

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

Dengan kata lain, tangkapan penutup pertama yang dapat menangani pengecualian berlaku, dan jika pengecualian dikeluarkan dari tangkapan itu, itu tidak dalam lingkup tangkapan lain untuk percobaan awal, sehingga mereka tidak akan mencoba untuk menanganinya.

Satu hal yang berhubungan dan membingungkan untuk diketahui adalah bahwa pada struktur try-[catch]-akhirnya, blok akhirnya dapat melempar pengecualian dan jika demikian, setiap pengecualian yang dilemparkan oleh try atau catch block hilang. Itu bisa membingungkan saat pertama kali Anda melihatnya.

Alex Miller
sumber
Hanya ingin menambahkan itu sejak Java 7 Anda dapat menghindari itu menggunakan try-dengan-sumber daya. Kemudian, jika tryDAN finallykeduanya melempar, finallyditekan, tetapi juga TAMBAH untuk pengecualian dari try. Jika catchmelempar juga, Anda tidak beruntung, kecuali Anda menanganinya sendiri melalui 'addSuppressed' dan menambahkan trypengecualian - maka Anda memiliki ketiganya.
LAFK mengatakan Reinstate Monica
6

Jika Anda ingin melempar pengecualian dari blok tangkap, Anda harus memberi tahu metode / kelas / dll Anda. bahwa itu perlu membuang kata pengecualian. Seperti itu:

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

Dan sekarang kompiler Anda tidak akan meneriaki Anda :)

Mastergeek
sumber
4

Tidak - Seperti kata Chris Jester-Young, itu akan dilemparkan ke try-catch berikutnya dalam hierarki.

Ian P
sumber
2

Seperti yang dikatakan di atas ...
Saya akan menambahkan bahwa jika Anda mengalami kesulitan melihat apa yang terjadi, jika Anda tidak dapat mereproduksi masalah di debugger, Anda dapat menambahkan jejak sebelum melemparkan kembali pengecualian baru (dengan Sistem lama yang baik .out.println lebih buruk, dengan sistem log yang bagus seperti log4j sebaliknya).

PhiLho
sumber
2

Itu tidak akan ditangkap oleh blok tangkapan kedua. Setiap Pengecualian hanya tertangkap saat berada di dalam blok percobaan. Anda dapat mencoba sarang (bukan itu ide yang bagus secara umum):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}
Vinko Vrsalovic
sumber
Saya menulis kode yang sama. Tapi saya tidak yakin. Saya ingin memanggil Thread.sleep () di blok tangkap saya. Tapi Thread.sleep melemparkan dirinya InterruptedException. Apakah benar (praktik terbaik) untuk melakukannya seperti yang Anda tunjukkan dalam contoh Anda?
riroo
1

Tidak, karena semua tangkapan mengacu pada blok uji coba yang sama, jadi melempar dari dalam blok tangkap akan ditangkap oleh blok uji coba yang terlampir (mungkin dalam metode yang disebut ini)

Uri
sumber
-4

Posting lama tetapi variabel "e" harus unik:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}
Ted K
sumber
5
Ini harus menjadi komentar dan bukan jawaban. Ini tidak memberikan apa pun dalam cara menjawab pertanyaan aktual - itu hanya kritik terhadap pertanyaan OP.
Derek W