Exception lancée dans la clause catch et finally

155

Sur une question pour Java à l'université, il y avait cet extrait de code:

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

On m'a demandé de donner sa sortie. J'ai répondu 13Exception in thread main MyExc2, mais la bonne réponse est 132Exception in thread main MyExc1. Qu'est-ce que c'est? Je ne comprends tout simplement pas où MyExc2va.

Jubstuff
la source

Réponses:

167

En lisant votre réponse et en voyant comment vous l'avez probablement trouvée, je pense que vous pensez qu'une «exception en cours» a une «priorité». Gardez à l'esprit:

Lorsqu'une nouvelle exception est levée dans un bloc catch ou un bloc finally qui se propage hors de ce bloc, alors l'exception actuelle sera abandonnée (et oubliée) lorsque la nouvelle exception est propagée vers l'extérieur. La nouvelle exception commence à dérouler la pile comme toute autre exception, abandonnant le bloc actuel (le bloc catch ou finally) et soumise à toute capture ou bloc finalement applicable en cours de route.

Notez que les blocs catch ou finally applicables incluent:

Lorsqu'une nouvelle exception est levée dans un bloc catch, la nouvelle exception est toujours soumise au bloc finally de cette catch, le cas échéant.

Maintenant, retracez l'exécution en vous rappelant que, chaque fois que vous frappez throw, vous devez abandonner le traçage de l'exception actuelle et commencer le traçage de la nouvelle exception.

Bert F
la source
7
«En lisant votre réponse et en voyant comment vous l'avez probablement
trouvée
39

Voici ce que Wikipedia dit à propos de la clause finally:

Plus courante est une clause associée (enfin, ou assurer) qui est exécutée qu'une exception se soit produite ou non, généralement pour libérer les ressources acquises dans le corps du bloc de gestion des exceptions.

Décortiquons votre programme.

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

Donc, 1sera affiché à l'écran, puis q()est appelé. Dans q(), une exception est levée. L'exception est alors interceptée Exception ymais elle ne fait rien. Une clause finally est alors exécutée (c'est 3nécessaire ), donc, sera imprimée à l'écran. Parce que (dans la méthode, q()il y a une exception levée dans la clause finally , la q()méthode transmet également l'exception à la pile parent (par la throws Exceptiondéclaration de méthode) new Exception()sera levée et interceptée par catch ( Exception i ), l' MyExc2exception sera levée (pour l'instant, ajoutez-la à la pile d'exceptions ), mais un enfin dans le mainbloc sera exécuté en premier.

Alors dedans,

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

Une clause finally est appelée ... (rappelez-vous, nous venons juste d'attraper Exception iet de jeter MyExc2) essentiellement, 2est imprimée à l'écran ... et après l' 2impression de la à l'écran, une MyExc1exception est levée. MyExc1est géré par la public static void main(...)méthode.

Production:

"132Exception dans le thread principal MyExc1"

Le conférencier a raison! :-)

En substance , si vous avez un finally dans une clause try / catch, un finally sera exécuté ( après avoir intercepté l'exception avant de lever l'exception interceptée)

Buhake Sindi
la source
Le catchest exécuté depuis q()jeté un Exceptionde son propre finallybloc.
Péter Török
"Dans q (), une exception est levée mais avant que l'exception ne soit complètement levée, une clause finally est d'abord exécutée, donc, 3 sera affichée à l'écran." Euh ... non, la première exception lancée dans qpasse l'exécution au catchbloc vide dans q(qui avale cette exception), puis au finallybloc dans q. Dit enfin le bloc imprime 3, puis lève une nouvelle exception, qui grâce à q's throws Exceptionest transmise à la pile au parent.
Powerlord
38

Les exceptions dans le bloc finally remplacent les exceptions dans le bloc catch.

Citant l' édition 14 de la spécification du langage Java :

Si le bloc catch se termine brusquement pour la raison R, alors le bloc finally est exécuté. Ensuite, il y a un choix:

  • Si le bloc finally se termine normalement, alors l'instruction try se termine brusquement pour la raison R.

  • Si le bloc finally se termine brusquement pour la raison S, alors l'instruction try se termine brusquement pour la raison S (et la raison R est rejetée).

Roland
la source
21

Enfin, la clause est exécutée même lorsqu'une exception est lancée de n'importe où dans le bloc try / catch.

Parce que c'est le dernier à être exécuté dans le mainet qu'il lève une exception, c'est l'exception que les appelants voient.

D'où l'importance de s'assurer que la finallyclause ne jette rien, car elle peut avaler des exceptions du tryblocage.

Alexander Pogrebnyak
la source
5
Il sera également exécuté MÊME s'il n'y a pas d'exception lancée dans le bloc try / catch
nanda
2
+1: Direct et au point sans serpenter sur toute la pile que l'OP semble déjà comprendre.
Powerlord
9

A methodne peut pas faire throwdeux exceptions en même temps. Il lancera toujours le dernier lancé exception, qui dans ce cas sera toujours celui du finallybloc.

Lorsque la première exception de la méthode q()est levée, elle sera interceptée puis avalée par l'exception finalement bloquée.

q () -> jeté new Exception -> main catch Exception -> throw new Exception -> finally lancer un nouveau exception(et celui de la catchest "perdu")

Garis M Suero
la source
3

La manière la plus simple de penser à cela est d'imaginer qu'il existe une variable globale à l'ensemble de l'application qui contient l'exception actuelle.

Exception currentException = null;

Lorsque chaque exception est levée, "currentException" est défini sur cette exception. Lorsque l'application se termine, si currentException est! = Null, le moteur d'exécution signale l'erreur.

De plus, les blocs finally s'exécutent toujours avant la fin de la méthode. Vous pouvez ensuite demander à l'extrait de code:

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'ordre dans lequel l'application s'exécute est:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}
Codage avec Spike
la source
1

Il est bien connu que le bloc finally est exécuté après le try and catch et qu'il est toujours exécuté ... ne faites pas toujours ce qu'ils devraient faire dans l'ordre que nous attendons du thème.

À votre santé.

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

}
////////////////////////////////////////////////// 
Sournois
la source
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();
    }
}

Ordre:

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
la source
1
Bien que cet extrait de code puisse être la solution, inclure une explication contribue vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code
Rahul Gupta
1

La logique est claire jusqu'à la fin de l'impression 13. Puis l'exception jeté q()est pris par catch (Exception i)dans main()et new MyEx2()est prêt à être jeté. Cependant, avant de lever l'exception, le finallybloc doit d'abord être exécuté. Ensuite, la sortie devient 132et finallydemande de lever une autre exception new MyEx1().

Comme une méthode ne peut pas en lancer plus d'un Exception, elle lancera toujours la dernière Exception. En d'autres termes, si les deux blocs catchet finallyessaient de lancer Exception, alors la Exceptionprise d' entrée est avalée et seule l'exception d' finallyentrée sera lancée.

Ainsi, dans ce programme, Exception MyEx2est avalée et MyEx1levée. Cette exception est levée main()et n'est plus interceptée, ainsi la JVM s'arrête et la sortie finale est 132Exception in thread main MyExc1.

En substance, si vous avez une clause finallyin a try/catch, a finallysera exécuté APRÈS avoir intercepté l'exception , mais AVANT de lancer une exception interceptée , et SEULE la dernière exception sera levée à la fin .

yyFred
la source
0

Je pense qu'il suffit de parcourir les finallyblocs:

  1. Imprimez "1".
  2. finallydans l' qimpression « 3 ».
  3. finallyen maincaractères d'imprimerie "2".
Uwe Keim
la source
0

Pour gérer ce genre de situation, c'est-à-dire gérer l'exception levée par le bloc finally. Vous pouvez entourer le bloc finally par try block: Regardez l'exemple ci-dessous en 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"

la source
-1

Je pense que cela résout le problème:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}
Vouze
la source
3
Quel problème allez-vous «résoudre»? Voulez-vous dire la question de l'examen? eh bien il a déjà répondu. Si vous parlez du problème du code donné, puisqu'il ne s'agit que d'une question d'examen, il n'y a aucun sens à le blâmer.
Earth Engine