web-dev-qa-db-fra.com

Exception levée à l'intérieur du bloc de prise - sera-t-il rattrapé?

Cela peut sembler une question de programmation 101 et je pensais connaître la réponse, mais je me trouve maintenant dans l'obligation de vérifier. Dans cette partie de code ci-dessous, l'exception levée dans le premier bloc catch sera-t-elle interceptée par le bloc catch général Exception ci-dessous?

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

J'ai toujours pensé que la réponse serait non, mais maintenant j'ai un comportement étrange qui pourrait être causé par cela. La réponse est probablement la même pour la plupart des langues, mais je travaille en Java.

162
roryf

Non, car le nouveau throw ne se trouve pas directement dans le bloc try.

197
Chris Jester-Young

Non, c'est très facile à vérifier.

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

Devrait imprimer:

 Dans catch IOException: classe Java.io.IOException 
 In Enfin 
 Exception dans le thread "principal" Java.lang.RuntimeException 
 À Catch.main (Catch. Java: 8)

Techniquement, cela aurait pu être un bogue du compilateur, un comportement dépendant de la mise en œuvre, non spécifié, ou quelque chose du genre. Cependant, le JLS est assez bien établi et les compilateurs sont assez bons pour ce genre de choses simples (le cas des génériques peut être différent).

Notez également que si vous permutez les deux blocs catch, la compilation n’est pas effectuée. La deuxième capture serait complètement inaccessible.

Notez que le bloc finally s'exécute toujours, même si un bloc catch est exécuté (autres que les cas idiots, tels que des boucles infinies, la liaison via l'interface des outils et la suppression du thread, la réécriture du bytecode, etc.).

63

La spécification de langage Java indique à la section 14.19.1:

Si l'exécution du bloc try se termine brusquement à cause d'un rejet d'une valeur V, vous avez le choix:

  • Si le type d'exécution de V est assignable au paramètre de toute clause catch de l'instruction try, la première (la plus à gauche) de cette clause catch est sélectionnée. La valeur V est affectée au paramètre de la clause catch sélectionnée et le bloc de cette clause catch est exécuté. Si ce bloc se termine normalement, l'instruction try se termine normalement; si ce bloc se termine brusquement pour une raison quelconque, l'instruction try se termine brusquement pour la même raison.

Référence: http://Java.Sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

En d’autres termes, la première capture englobante capable de gérer l’exception le fait, et si une exception est renvoyée de cette capture, elle n’entre pas dans la portée de toute autre capture de l’essai d’origine, de sorte qu’ils ne tenteront pas de la gérer.

Une chose liée et déroutante à savoir est que, dans une structure try- [catch] -finally, un bloc finally peut générer une exception et, le cas échéant, toute exception levée par le bloc try ou catch est perdue. Cela peut être déroutant la première fois que vous le voyez.

26
Alex Miller

Si vous voulez lever une exception du bloc catch, vous devez informer votre méthode/classe/etc. qu'il faut jeter cette exception. Ainsi:

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

Et maintenant, votre compilateur ne va pas vous crier dessus :)

6
Mastergeek

Non - Comme l'a dit Chris Jester-Young, cela sera reporté à la prochaine tentative dans la hiérarchie.

4
Ian P

Comme dit ci-dessus ...
J'ajouterais que si vous avez du mal à voir ce qui se passe, si vous ne pouvez pas reproduire le problème dans le débogueur, vous pouvez ajouter une trace avant de relancer la nouvelle exception (avec le bon vieux système. out.println au pire, avec un bon système de journalisation comme log4j sinon).

1
PhiLho

Il ne sera pas attrapé par le deuxième bloc catch. Chaque exception n'est interceptée que dans un bloc try. Vous pouvez faire un nid d'essais (ce n'est pas une bonne idée en général):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}
1
Vinko Vrsalovic

Non, puisque les captures se rapportent toutes au même bloc try, de sorte que le lancer depuis un bloc catch serait capturé par un bloc try inclus (probablement dans la méthode qui a appelé celui-ci).

1
Uri