Eksepsi dilemparkan ke tangkapan dan akhirnya klausa

155

Pada pertanyaan untuk Java di universitas, ada cuplikan kode ini:

class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print(1);
            q();
        }
        catch (Exception i) {
            throw new MyExc2();
        }
        finally {
            System.out.print(2);
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            throw new MyExc1();
        }
        catch (Exception y) {
        }
        finally {
            System.out.print(3);
            throw new Exception();
        }
    }
}

Saya diminta memberikan hasilnya. Saya menjawab 13Exception in thread main MyExc2, tetapi jawaban yang benar adalah 132Exception in thread main MyExc1. Kenapa begitu? Saya tidak bisa mengerti ke mana perginya MyExc2.

Jubstuff
sumber

Jawaban:

167

Berdasarkan pada membaca jawaban Anda dan melihat bagaimana Anda mungkin muncul dengan itu, saya percaya Anda berpikir "pengecualian dalam proses" memiliki "prioritas". Mengingat:

Ketika eksepsi baru dilemparkan ke dalam blok tangkap atau akhirnya blok yang akan merambat keluar dari blok itu, maka eksepsi saat ini akan dibatalkan (dan dilupakan) karena eksepsi baru disebarkan ke luar. Pengecualian baru mulai membuka tumpukan sama seperti pengecualian lainnya, membatalkan keluar dari blok saat ini (tangkapan atau akhirnya blok) dan tunduk pada tangkapan yang berlaku atau akhirnya blok di sepanjang jalan.

Perhatikan bahwa tangkapan yang berlaku atau akhirnya blok meliputi:

Ketika pengecualian baru dilemparkan ke dalam blok tangkap, pengecualian baru masih tunduk pada blok tangkap itu, jika ada.

Sekarang telusuri kembali eksekusi dengan mengingat bahwa, setiap kali Anda menekan throw, Anda harus membatalkan penelusuran pengecualian saat ini dan mulai melacak pengecualian baru.

Bert F
sumber
7
«Berdasarkan pada membaca jawaban Anda dan melihat bagaimana Anda mungkin muncul dengan itu, saya percaya Anda berpikir" pengecualian dalam proses "memiliki" diutamakan "» Terima kasih ... itu persis seperti yang saya pikirkan :)
Jubstuff
39

Inilah yang dikatakan Wikipedia tentang akhirnya klausa:

Yang lebih umum adalah klausa terkait (akhirnya, atau memastikan) yang dieksekusi apakah pengecualian terjadi atau tidak, biasanya untuk melepaskan sumber daya yang diperoleh dalam tubuh blok penanganan pengecualian.

Mari kita membedah program Anda.

try {
    System.out.print(1);
    q();
}

Jadi, 1akan di-output ke layar, lalu q()dipanggil. Di q(), pengecualian dilemparkan. Pengecualian kemudian ditangkap Exception ytetapi tidak melakukan apa pun. Sebuah akhirnya klausul kemudian dieksekusi (itu), jadi, 3akan dicetak ke layar. Karena (dalam metode q()ada pengecualian yang dilemparkan dalam klausa terakhir , juga q()metode yang melewatkan pengecualian ke tumpukan induk (dengan throws Exceptiondi dalam deklarasi metode) new Exception()akan dibuang dan ditangkap oleh catch ( Exception i ), MyExc2pengecualian akan dilemparkan (untuk saat ini tambahkan ke tumpukan pengecualian ), tetapi akhirnya di mainblok akan dieksekusi terlebih dahulu.

Jadi di,

catch ( Exception i ) {
    throw( new MyExc2() );
} 
finally {
    System.out.print(2);
    throw( new MyExc1() );
}

Sebuah akhirnya klausa disebut ... (ingat, kita baru saja tertangkap Exception idan dibuang MyExc2) pada dasarnya, 2dicetak pada layar ... dan setelah 2dicetak di layar, sebuah MyExc1pengecualian dilemparkan. MyExc1ditangani dengan public static void main(...)metode ini.

Keluaran:

"132 Pengecualian di utas utama MyExc1"

Dosen benar! :-)

Pada intinya , jika Anda memiliki akhirnya dalam klausa try / catch, finally akan dieksekusi ( setelah menangkap pengecualian sebelum melemparkan pengecualian tertangkap keluar)

Buhake Sindi
sumber
Ini catchdieksekusi sejak q()melemparkan sebuah Exceptiondari finallybloknya sendiri .
Péter Török
"Dalam q (), pengecualian dilemparkan tetapi sebelum pengecualian dilempar sepenuhnya, klausa akhirnya pertama kali dieksekusi, jadi, 3 akan dicetak ke layar." Er ... tidak, pengecualian pertama yang dilemparkan qmelalui eksekusi ke catchblok kosong di q(yang menelan pengecualian ini), lalu ke finallyblok dalam q. Kata akhirnya blok cetakan 3, kemudian melemparkan pengecualian baru, yang berkat q's throws Exceptiondilewatkan sampai tumpukan ke orangtua.
Powerlord
38

Pengecualian di blok akhirnya menggantikan pengecualian di blok tangkap.

Mengutip dari Bahasa Jawa Spesifikasi 14 edisi :

Jika blok tangkap selesai tiba-tiba karena alasan R, maka blok akhirnya dijalankan. Lalu ada pilihan:

  • Jika blok akhirnya selesai secara normal, maka pernyataan coba selesai tiba-tiba karena alasan R.

  • Jika blok akhirnya selesai tiba-tiba karena alasan S, maka pernyataan coba lengkap dengan alasan S (dan alasan R dibuang).

Roland
sumber
21

Akhirnya klausa dieksekusi bahkan ketika pengecualian dilempar dari mana saja di blok try / catch.

Karena itu adalah yang terakhir dieksekusi di dalam maindan itu melempar pengecualian, itulah pengecualian yang dilihat penelepon.

Oleh karena itu pentingnya memastikan bahwa finallyklausa tidak membuang apa pun, karena dapat menelan pengecualian dari tryblok.

Alexander Pogrebnyak
sumber
5
Ini juga akan dieksekusi BAHKAN jika tidak ada pengecualian dilemparkan di blok try / catch
nanda
2
+1: Mengarahkan dan langsung ke titik tanpa menyusuri seluruh tumpukan yang tampaknya dipahami OP.
Powerlord
9

Sebuah methodtidak bisa throwdua pengecualian pada waktu yang sama. Itu akan selalu membuang yang terakhir terlempar exception, yang dalam hal ini akan selalu menjadi yang dari finallyblok.

Ketika pengecualian pertama dari metode q()dilempar, ia akan ditangkap dan kemudian ditelan oleh pengecualian blok akhirnya dilempar.

q () -> dibuang new Exception -> main catch Exception -> throw new Exception -> finally melempar yang baru exception(dan yang dari catch"hilang")

Garis M Suero
sumber
3

Cara termudah untuk memikirkan ini adalah membayangkan bahwa ada variabel global ke seluruh aplikasi yang menyimpan pengecualian saat ini.

Exception currentException = null;

Karena setiap pengecualian dilemparkan, "currentException" diatur ke pengecualian itu. Ketika aplikasi berakhir, jika currentException! = Null, maka runtime melaporkan kesalahan.

Juga, blok akhirnya selalu berjalan sebelum metode keluar. Anda kemudian dapat membalas potongan kode ke:

public class C1 {

    public static void main(String [] argv) throws Exception {
        try {
            System.out.print(1);
            q();

        }
        catch ( Exception i ) {
            // <-- currentException = Exception, as thrown by q()'s finally block
            throw( new MyExc2() ); // <-- currentException = MyExc2
        }
        finally {
             // <-- currentException = MyExc2, thrown from main()'s catch block
            System.out.print(2);
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }

    }  // <-- At application exit, currentException = MyExc1, from main()'s finally block. Java now dumps that to the console.

    static void q() throws Exception {
        try {
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }
        catch( Exception y ) {
           // <-- currentException = null, because the exception is caught and not rethrown
        }
        finally {
            System.out.print(3);
            throw( new Exception() ); // <-- currentException = Exception
        }
    }
}

Urutan penerapan aplikasi adalah:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}
CodingWithSpike
sumber
1

Diketahui bahwa blok akhirnya dieksekusi setelah coba dan tangkap dan selalu dieksekusi .... Tapi seperti yang Anda lihat itu agak rumit kadang-kadang memeriksa potongan kode di bawah ini dan Anda akan mengatakan bahwa pernyataan pengembalian dan melempar tidak selalu melakukan apa yang harus mereka lakukan sesuai urutan tema yang kita harapkan.

Bersulang.

/////////////Return dont always return///////

try{

    return "In Try";

}

finally{

    return "In Finally";

}

////////////////////////////////////////////


////////////////////////////////////////////    
while(true) { 

    try {

        return "In try";

   } 

   finally{

        break;     

    }          
}              
return "Out of try";      
///////////////////////////////////////////


///////////////////////////////////////////////////

while (true) {     

    try {            

        return "In try";    

     } 
     finally {   

         continue;  

     }                         
}
//////////////////////////////////////////////////

/////////////////Throw dont always throw/////////

try {

    throw new RuntimeException();

} 
finally {

    return "Ouuuups no throw!";

}
////////////////////////////////////////////////// 
Licik
sumber
1
class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print("TryA L1\n");
            q();
            System.out.print("TryB L1\n");
        }
        catch (Exception i) {
            System.out.print("Catch L1\n");                
        }
        finally {
            System.out.print("Finally L1\n");
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            System.out.print("TryA L2\n");
            q2();
            System.out.print("TryB L2\n");
        }
        catch (Exception y) {
            System.out.print("Catch L2\n");
            throw new MyExc2();  
        }
        finally {
            System.out.print("Finally L2\n");
            throw new Exception();
        }
    }

    static void q2() throws Exception {
        throw new MyExc1();
    }
}

Memesan:

TryA L1
TryA L2
Catch L2
Finally L2
Catch L1
Finally L1        
Exception in thread "main" MyExc1 at C1.main(C1.java:30)

https://www.compilejava.net/

Luiz Fernando
sumber
1
Sementara cuplikan kode ini mungkin solusinya, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda
Rahul Gupta
1

Logikanya jelas sampai selesai dicetak 13. Kemudian pengecualian dilemparkan q()ditangkap oleh catch (Exception i)dalam main()dan new MyEx2()siap untuk dilempar. Namun, sebelum melempar pengecualian, finallyblok harus dieksekusi terlebih dahulu. Kemudian output menjadi 132dan finallymeminta untuk melemparkan pengecualian lain new MyEx1().

Karena metode tidak dapat melempar lebih dari satu Exception, ia akan selalu membuang yang terbaru Exception. Dengan kata lain, jika keduanya catchdan finallybalok mencoba untuk melempar Exception, maka Exceptiontangkapan yang masuk ditelan dan hanya pengecualian yang finallyakan dilemparkan.

Dengan demikian, dalam program ini, Pengecualian MyEx2ditelan dan MyEx1dibuang. Pengecualian ini dibuang main()dan tidak lagi tertangkap, sehingga JVM berhenti dan hasil akhirnya 132Exception in thread main MyExc1.

Intinya, jika Anda memiliki finallydalam try/catchklausa, a finallyakan dieksekusi SETELAH menangkap pengecualian , tetapi SEBELUM melempar pengecualian yang tertangkap , dan HANYA pengecualian terakhir akan dilemparkan pada akhirnya .

yyFred
sumber
0

Saya pikir Anda hanya harus berjalan di finallyblok:

  1. Cetak "1".
  2. finallydi qcetak "3".
  3. finallydi maincetak "2".
Uwe Keim
sumber
0

Untuk menangani situasi semacam ini yaitu menangani pengecualian yang diajukan oleh akhirnya blok. Anda dapat mengelilingi blok akhirnya dengan mencoba blok: Lihat contoh di bawah ini dengan python:

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print "Going to close the file"
      fh.close()
except IOError:
   print "Error: can\'t find file or read data"

sumber
-1

Saya pikir ini menyelesaikan masalah:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}
Vouze
sumber
3
Masalah apa yang akan Anda "selesaikan"? Apakah maksud Anda pertanyaan dalam ujian? baik itu sudah dijawab. Jika yang Anda maksud adalah masalah kode yang diberikan, karena ini hanya soal ujian, tidak ada alasan untuk menyalahkannya.
Earth Engine