Exception lancée à l'intérieur du bloc catch - sera-t-il rattrapé?

180

Cela peut sembler être une question de programmation 101 et j'avais pensé connaître la réponse, mais j'ai maintenant besoin de vérifier. Dans ce morceau de code ci-dessous, l'exception lancée dans le premier bloc catch sera-t-elle interceptée par le bloc général Exception catch 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 langages mais je travaille en Java.

roryf
la source
2
Peut-être pourriez-vous décrire le «comportement étrange»?
Jeffrey L Whitledge le
Êtes-vous sûr que l'exception ApplicationException n'est pas lancée ailleurs et ne se propage pas jusqu'à ce bloc?
sblundy le
J'ai remarqué cela dans mon IDE Eclipse. C'est tyrannique de me forcer à mettre la "nouvelle exception" dans un bloc try mais je ne sais pas pourquoi. Je l'ai fait dans le passé sans le faire. Je ne vois pas pourquoi un bloc try serait nécessaire. De nombreux exemples sur Google montrent des personnes qui n'ont pas besoin d'un bloc d'essai. Est-ce parce que je jette à l'intérieur une déclaration if?
djangofan

Réponses:

214

Non, puisque le nouveau thrown'est pas trydirectement dans le bloc.

Chris Jester-Young
la source
Disons si le code est comme ça try {} catch (Exception e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("In catch IOException:" + e.getClass ()); } et le code dans le bloc try génère une exception IO, ira-t-il au bloc Exception général immédiat ou il survolera le bloc catch IOException?
sofs1
4
@ user3705478 Le code ne se compile pas, pour éviter ce genre de situation erronée. En général, vous n'êtes pas autorisé à avoir une capture pour une sous-classe après une capture de sa superclasse dans le même bloc try.
Chris Jester-Young
Merci. J'obtiendrai donc une erreur de compilation, non? Je vais le tester quand je rentre à la maison.
sofs1
Cela dépend @ user3705478, si a RuntimeExceptionest renvoyé du catchbloc, il n'y aura pas d'erreur de compilation.
Randy the Dev
@AndrewDunn Je ne pense pas que ce soit la question de user3705478, mais plutôt ce qui se passe si une catchclause d' exception parent est répertoriée avant une catchclause d' exception enfant . Je crois comprendre que Java interdit cela et qu'il est intercepté au moment de la compilation.
Chris Jester-Young
71

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
En enfin
Exception dans le thread "main" java.lang.RuntimeException
        à Catch.main (Catch.java:8)

Techniquement, cela aurait pu être un bogue du compilateur, un comportement dépendant de l'implémentation, un comportement non spécifié ou quelque chose. Cependant, le JLS est assez bien défini et les compilateurs sont assez bons pour ce genre de chose simple (le cas du coin des génériques peut être une autre affaire).

Notez également que si vous permutez les deux blocs catch, il ne se compilera pas. 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é (sauf dans les cas idiots, comme les boucles infinies, l'attachement via l'interface des outils et la suppression du thread, la réécriture du bytecode, etc.).

Tom Hawtin - ligne de pêche
la source
3
Le moyen le plus évident d'éviter un finallyest, bien sûr, d'appeler System.exit. :-P
Chris Jester-Young
3
@Chris Jester-Young for(;;);est plus court, contenu dans la langue, n'introduit pas beaucoup d'effets secondaires et, pour moi, plus évident.
Tom Hawtin - tackline
1
System.exitest plus convivial pour le processeur! : -O Mais oui, d'accord, c'est clairement un critère subjectif. De plus, je ne savais pas que vous étiez un golfeur de code. ;-)
Chris Jester-Young
28

La spécification du langage Java dit dans la section 14.19.1:

Si l'exécution du bloc try se termine brusquement à cause d'un jet d'une valeur V, alors il y a un choix:

  • Si le type d'exécution de V est attribuable au paramètre de n'importe quelle clause catch de l'instruction try, la première clause catch (la plus à gauche) 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, alors 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 qui peut gérer l'exception le fait, et si une exception est levée hors de cette capture, ce n'est pas dans la portée de toute autre capture pour l'essai d'origine, donc ils n'essaieront pas de la gérer.

Une chose connexe et déroutante à savoir est que dans une structure try- [catch] -finally, un bloc finally peut lever une exception et si c'est le cas, 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.

Alex Miller
la source
Je voulais juste ajouter que depuis Java 7, vous pouvez éviter d'utiliser try-with-resources. Ensuite, si tryET les finallydeux lancent, finallyest supprimé, mais est également AJOUTÉ à l'exception de try. Si catchvous lancez également, vous n'avez pas de chance, à moins que vous ne gériez cela vous-même via 'addSuppressed' et en ajoutant l' tryexception - alors vous avez les trois.
LAFK dit Réintégrer Monica le
6

Si vous voulez lever une exception du bloc catch, vous devez informer votre méthode / classe / etc. qu'il a besoin de lever ladite exception. Ainsi:

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

Et maintenant, votre compilateur ne vous criera plus dessus :)

Mastergeek
la source
4

Non - Comme Chris Jester-Young l'a dit, il sera jeté au prochain essai dans la hiérarchie.

Ian P
la source
2

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).

PhiLho
la source
2

Il ne sera pas attrapé par le deuxième bloc catch. Chaque exception est interceptée uniquement à l'intérieur d'un bloc try. Vous pouvez cependant imbriquer des 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) {
}
Vinko Vrsalovic
la source
J'ai écrit un code similaire. Mais je ne suis pas convaincu. Je veux appeler un Thread.sleep () dans mon bloc catch. Mais Thread.sleep se lance une InterruptedException. Est-il juste (une bonne pratique) de le faire comme vous l'avez montré dans votre exemple?
riroo
1

Non, puisque les captures se réfèrent toutes au même bloc try, donc lancer depuis un bloc catch serait intercepté par un bloc try englobant (probablement dans la méthode qui a appelé celui-ci)

Uri
la source
-4

Ancien message, mais la variable "e" doit être unique:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}
Ted K
la source
5
Cela devrait être un commentaire et non une réponse. Il n'apporte rien pour aborder la question réelle - c'est juste une critique de la question des PO.
Derek W