avr.
2008
Java Quiz #7
Que fait le code suivant ?
public class Parser { public static int parse(String valeur) { try { return Integer.parseInt(valeur); } catch (NumberFormatException nfEx) { return 2; } finally { return 3; } } public static void main(String... args) { System.out.println(parse("1")); System.out.println(parse("1.0")); } }
Réponse :
Ce code affiche
3 3
La spécification du langage Java montre clairement que, quel que soit le résultat des blocs try et catch, le bloc finally sera toujours exécuté, et décidera du résultat final de l'expression.
En particulier :
If the finally block completes abruptly for any reason, then the try statement completes abruptly for the same reason.
Ce qui signifie que, si le bloc finally exécute un return ou lance une exception, l'ensemble du bloc try-catch-finally se termine sur ce return ou cette exception. Conséquence : les valeurs retournées et les exceptions lancées depuis les blocs try et catch sont alors tout simplement ignorés.
Dans notre exemple :
- Le premier appel à notre méthode parse() est effectué avec une chaîne représentant correctement un entier. Integer.parseInt() ne génère pas d'erreur, et le return est effectué. Mais avant de rendre la main à son appelant, la méthode exécute le bloc finally associé. Celui-ci effectue un return qui "écrase" alors est alors celui du bloc try. La valeur renvoyée est donc 3.
- Le second appel est effectué avec une valeur qui met Integer.parseInt() en erreur. Le bloc catch est donc exécuté, et se prépare à renvoyer 2. Mais là encore le bloc finally est exécuté, et c'est encore la valeur 3 qui est renvoyée.
Conclusion : Il ne faut jamais exécuter de return ni lancer d'exceptions depuis un bloc finally !
Application pratique :
Lors de la lecture d'un fichier, le bloc finally sert à fermer le flux. Or, la méthode close() peut lever une IOException. Il est donc nécessaire de capturer cette exception pour éviter qu'elle ne soit renvoyée par le bloc finally.
public static void readFile(String file) { FileReader reader = null; try { reader = new FileReader(file); // Lire le fichier } catch (IOException ioEx) { // Traiter l'exception de lecture } finally { try { if (reader != null) { reader.close(); } } catch (IOException ex) { // Loguer l'exception de fermeture du flux // Il ne faut surtout pas relancer cette exception ! } } }
Commentaires
heu j'aurais tendance à dire :
3
3
Mais j'hésite avec une Exception à la compilation ou à l'exécution... (je vais de ce pas copier/coller le code mais je dirais rien...)
mmmh ok j'ai fais le test sous eclipse et j'ai la réponse. Mais je me l'explique pas trop, vivement la réponse! :)
Est-ce qu'on n'avait pas dit un jour de ne jamais mettre de return dans un bloc finally ?
Si, justement :)
Te rappelles-tu pourquoi ?
Si je me souviens bien c'est pour garantir l'exécution de toutes les intstructions du bloc sans encombre. Avec un return ou un throw dans le bloc finally le comportement du code est imprévisible.
NEEEEEEETTT !!!!
Le comportement de ce code est parfaitement prévisible. C'est juste qu'il est, disons... inhabituel.
Même joueur, joue encore... :-)))
Oue bon, mes pseudo t'ont pas plu? Je suis devenu monsieur inconnu :)
Bon, plus sérieusement, je ne comprends pas pourquoi ne pas relancer l'exception dans le finally. Si pour une raison où une autre, on ne veux pas risquer de rester avec un flux ouvert? on peut vouloir cette erreur bloquante, non?