Eccezione lanciata nella cattura e infine clausola


155

Su una domanda per Java all'università, c'era questo frammento di codice:

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

Mi è stato chiesto di dare il suo risultato. Ho risposto 13Exception in thread main MyExc2, ma la risposta corretta è 132Exception in thread main MyExc1. Perché è quello? Non riesco proprio a capire dove MyExc2vada.

Risposte:


167

Basandomi sulla lettura della tua risposta e sul modo in cui probabilmente ti è venuta in mente, credo che pensi che una "eccezione in corso" abbia "precedenza". Tieni a mente:

Quando una nuova eccezione viene generata in un blocco catch o infine in un blocco che si propagherà da quel blocco, allora l'eccezione corrente verrà interrotta (e dimenticata) mentre la nuova eccezione viene propagata verso l'esterno. La nuova eccezione inizia a srotolare lo stack come qualsiasi altra eccezione, interrompendo il blocco corrente (il blocco o infine il blocco) e soggetto a qualsiasi cattura applicabile o infine blocchi lungo il percorso.

Si noti che le catture applicabili o infine i blocchi includono:

Quando viene generata una nuova eccezione in un blocco catch, la nuova eccezione è ancora soggetta al blocco di tale cattura, se presente.

Ora ripercorri l'esecuzione ricordando che, ogni volta che colpisci throw, dovresti interrompere la traccia dell'eccezione corrente e iniziare a tracciare la nuova eccezione.


7
«Basandomi sulla lettura della tua risposta e sul modo in cui probabilmente ti è venuta in mente, credo che tu pensi che una" eccezione in corso "abbia" precedenza "» Grazie ... era esattamente il mio pensiero :)
Jubstuff,

39

Questo è ciò che Wikipedia dice sulla clausola finalmente:

Più comune è una clausola correlata (infine, o assicurata) che viene eseguita indipendentemente dal verificarsi di un'eccezione, in genere per rilasciare risorse acquisite all'interno del corpo del blocco di gestione delle eccezioni.

Analizziamo il tuo programma.

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

Quindi, 1verrà emesso sullo schermo, quindi q()viene chiamato. In q(), viene generata un'eccezione. L'eccezione viene quindi colta Exception yma non fa nulla. Viene quindi eseguita una clausola finally (deve), quindi 3verrà stampata sullo schermo. Poiché (nel metodo q()è stata generata un'eccezione nella clausola finally , anche il q()metodo passa l'eccezione allo stack principale (dalla throws Exceptiondichiarazione del metodo) new Exception()verrà generato e catturato catch ( Exception i ), MyExc2verrà generata l'eccezione (per ora aggiungerla allo stack delle eccezioni ), ma verrà infinemain eseguito per primo un blocco nel blocco.

Quindi dentro,

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

UN clausola finalmente si chiama ... (ricorda, abbiamo appena catturato Exception ie lanciato MyExc2) in sostanza, 2viene stampato sullo schermo ... e dopo che 2viene stampato sullo schermo, MyExc1viene generata un'eccezione. MyExc1è gestito dal public static void main(...)metodo

Produzione:

"132Exception nel thread MyExc1 principale"

Il conferenziere ha ragione! :-)

In sostanza , se si dispone di una finalmente in una clausola try / catch, verrà finalmente eseguita una ( dopo aver catturato l'eccezione prima rilevato eliminare l'eccezione rilevata)


Il catchviene eseguito da quando ha q()lanciato un Exceptiondal proprio finallyblocco.
Péter Török,

"In q (), viene generata un'eccezione ma prima che l'eccezione venga completamente lanciata, viene prima eseguita una clausola finally, quindi 3 verrà stampato sullo schermo." Ehm ... no, la prima eccezione generata qpassa l'esecuzione all'esecuzione catchblocco vuoto in q(che ingoia questa eccezione), quindi nel finallyblocco in q. Detto infine, blocca le stampe 3, quindi genera una nuova eccezione, che grazie a q's throws Exceptionpassa la pila al genitore.
Powerlord,

38

Le eccezioni nel blocco finally sostituiscono le eccezioni nel blocco catch.

Citando dalla Java Language Specification 14 edizione :

Se il blocco catch si completa improvvisamente per la ragione R, viene eseguito il blocco finally. Quindi c'è una scelta:

  • Se il blocco finally si completa normalmente, l'istruzione try si completa improvvisamente per il motivo R.

  • Se il blocco finally si completa bruscamente per il motivo S, l'istruzione try si completa bruscamente per il motivo S (e il motivo R viene scartato).


21

Infine, la clausola viene eseguita anche quando viene generata un'eccezione da qualsiasi punto del blocco try / catch.

Perché è l'ultimo ad essere eseguito nel main e genera un'eccezione, questa è l'eccezione che vedono i chiamanti.

Da qui l'importanza di assicurarsi che la finallyclausola non generi nulla, perché può ingoiare eccezioni dal tryblocco.


5
Sarà eseguito anche ANCHE se non viene generata alcuna eccezione nel blocco try / catch
nanda,

2
+1: diretto e al punto senza girovagare per l'intero stack che l'OP sembra già comprendere.
Powerlord,

9

A methodnon si possono fare throwdue eccezioni contemporaneamente. Lancerà sempre l'ultimo lancio exception, che in questo caso sarà sempre quello del finallyblocco.

Quando q()viene lanciata la prima eccezione dal metodo , verrà catturata e quindi inghiottita dall'eccezione infine generata dal blocco.

q () -> generato new Exception -> main catch Exception -> throw new Exception -> finally lancia un nuovo exception(e quello dell'elemento catchè "perso")


3

Il modo più semplice di pensare a questo è immaginare che ci sia una variabile globale per l'intera applicazione che contiene l'eccezione corrente.

Exception currentException = null;

Quando viene generata ogni eccezione, "currentException" viene impostato su tale eccezione. Al termine dell'applicazione, se currentException è! = Null, il runtime segnala l'errore.

Inoltre, i blocchi finally vengono sempre eseguiti prima della chiusura del metodo. È quindi possibile richiedere lo snippet di codice per:

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

L'ordine in cui viene eseguita l'applicazione è:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}

1

È noto che il blocco finally viene eseguito dopo il tentativo di cattura ed è sempre eseguito .... Ma come hai visto è un po 'complicato a volte dai un'occhiata a quel frammento di codice qui sotto e farai che le dichiarazioni di ritorno e di lancio don fai sempre quello che dovrebbero fare nell'ordine in cui ci aspettiamo che il tema.

Saluti.

/////////////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!";

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

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

Ordine:

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/


1
Sebbene questo frammento di codice possa essere la soluzione, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro, e quelle persone potrebbero non conoscere i motivi del tuo suggerimento sul codice
Rahul Gupta,

1

La logica è chiara fino al termine della stampa 13. Quindi l'eccezione generata q()viene catturata da catch (Exception i)in main()e a new MyEx2()è pronta per essere lanciata. Tuttavia, prima di lanciare l'eccezione, il finallyblocco deve essere eseguito per primo. Quindi l'output diventa 132e finallychiede di generare un'altra eccezione new MyEx1().

Poiché un metodo non può lanciarne più di uno Exception, lancerà sempre l'ultimo Exception. In altre parole, se entrambi catche finallyblocchi cercano di gettare Exception, poi l' Exceptionin cattura è inghiottito e solo l'eccezione infinally saranno gettati.

Pertanto, in questo programma, l'eccezione MyEx2viene ingerita e MyEx1generata. Questa eccezione viene eliminata main()e non viene più rilevata, quindi JVM si interrompe e l'output finale è 132Exception in thread main MyExc1.

In sostanza, se si dispone di una finallyin una try/catchclausola, una finallysarà eseguito dopo la cattura l'eccezione , ma prima di lanciare qualsiasi eccezione catturato , e solo l'eccezione lastest sarebbe gettato in alla fine .


0

Penso che devi solo camminare per i finallyblocchi:

  1. Stampa "1".
  2. finallynella qstampa "3".
  3. finallynella mainstampa "2".

0

Gestire questo tipo di situazione, ad esempio gestendo l'eccezione sollevata dal blocco infine. Puoi circondare il blocco finally dal blocco try: guarda l'esempio seguente in 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"

-1

Penso che questo risolva il problema:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}

3
Quale problema hai intenzione di "risolvere"? Intendi la domanda nell'esame? bene ha già risposto. Se intendi il problema di un determinato codice, dato che si tratta solo di una domanda d'esame, non ha senso biasimarlo.
Earth Engine
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.