Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 28

Gestion des exceptions avec try-catch-finally

Recommended resources

Bonjour ! Dans la leçon précédente, nous avons appris à identifier les différents types d'erreurs en Java en explorant la hiérarchie des exceptions. Vous savez maintenant faire la distinction essentielle entre les exceptions vérifiées (checked) et non-vérifiées (unchecked), ce qui est la base pour écrire du code robuste.

Aujourd'hui, nous passons de la théorie à la pratique. Savoir identifier une erreur est une chose, mais savoir comment la maîtriser pour que votre application ne plante pas en est une autre. L'objectif de cette leçon est donc de vous apprendre à gérer activement les exceptions à l'aide des trois mots-clés fondamentaux : try, catch et finally. C'est une compétence absolument critique pour un développeur backend, car elle garantit que vos applications peuvent gérer les imprévus avec élégance sans s'arrêter brutalement.

1. Le bloc try-catch : Intercepter une exception

Imaginez une portion de votre code qui est "à risque" : une division mathématique, une conversion de type, un accès à un tableau. Si une erreur se produit à cet endroit, par défaut, le programme s'arrête net et affiche une trace d'erreur (la stack trace) dans la console. Ce n'est pas un comportement acceptable pour une application professionnelle.

Le mécanisme try-catch permet de "surveiller" ce code à risque.

  • Le code potentiellement dangereux est placé dans un bloc try.
  • Si une exception se produit dans le bloc try, l'exécution de ce bloc s'arrête immédiatement et Java cherche un bloc catch correspondant.
  • Le bloc catch est un gestionnaire d'erreur : il définit le code à exécuter pour "rattraper" l'erreur et permettre au programme de continuer son exécution.

Les exceptions - Apprenez à programmer en Java - Zeste de Savoir

L'article "Les exceptions" sur Zeste de Savoir illustre parfaitement ce concept avec un exemple simple de division par zéro.

Lisez la section "Le bloc try{...} catch{...}". Observez bien la différence de comportement du programme avant et après l'introduction du bloc try-catch. Notez comment l'utilisation de catch permet au programme d'afficher "coucou toi !", ce qui était impossible lorsque l'exception n'était pas gérée. Concentrez-vous aussi sur l'utilisation de la méthode e.getMessage() pour obtenir une description de l'erreur.

Pour voir ce mécanisme en action, la vidéo suivante montre le flux d'exécution de manière très visuelle.

Apprendre Java #14 Exception (try catch finally)

La vidéo "Apprendre Java #14" de la chaîne JirAWS explique le fonctionnement du try-catch avec une analogie parlante.

Regardez ce passage qui montre le flux de contrôle avec et sans erreur : Démonstration du try-catch : Faites bien la distinction entre le cas où la division réussit (le catch est ignoré) et le cas où elle échoue (le catch est exécuté). Notez que la vidéo utilise error.toString() (implicitement) et mentionne la stack trace, tandis que l'article utilisait e.getMessage(). En pratique : e.printStackTrace(): Très utile pendant le développement pour déboguer. Elle imprime la pile d'appels complète de l'erreur. e.getMessage(): Utile pour obtenir un message d'erreur descriptif que vous pourriez, par exemple, logger ou afficher à l'utilisateur de manière contrôlée.


2. Visualiser le flux d'exécution

Le concept peut être résumé par le diagramme de flux suivant. Il montre clairement la bifurcation que prend votre programme lorsqu'une exception est levée.

Ce diagramme illustre le chemin d'exécution du code. L'exécution commence dans le bloc `try`. Si une exception se produit, le contrôle passe au bloc `catch`. Quoi qu'il arrive, le bloc `finally` est toujours exécuté à la fin. Nous allons voir ce dernier juste après.

3. Gérer plusieurs types d'exceptions

Un même bloc try peut potentiellement lever plusieurs types d'exceptions différents. Par exemple, un traitement sur un tableau pourrait lever une ArrayIndexOutOfBoundsException si l'index est invalide, ou une NumberFormatException si vous essayez de convertir un élément non numérique en int.

Java offre deux manières de gérer cela.

Approche 1 : Plusieurs blocs catch

Vous pouvez chaîner plusieurs blocs catch, chacun spécialisé dans la capture d'un type d'exception particulier. C'est utile si vous voulez effectuer un traitement différent pour chaque type d'erreur.

try {
    // Code pouvant lever plusieurs types d'exceptions
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Erreur : l'index du tableau est en dehors des limites.");
} catch (NumberFormatException e) {
    System.out.println("Erreur : impossible de convertir la chaîne en nombre.");
}

Approche 2 : Le multi-catch (depuis Java 7)

Si le traitement de l'erreur est identique pour plusieurs types d'exceptions, vous pouvez les regrouper dans un seul bloc catch en utilisant l'opérateur | (pipe). C'est plus concis et évite la duplication de code.

try {
    // Code pouvant lever plusieurs types d'exceptions
} catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
    System.out.println("Une erreur est survenue lors du traitement des données : " + e.getMessage());
}

La vidéo suivante démontre très bien ces deux approches.

APPRENDRE LE JAVA #13 - LES EXCEPTIONS

La vidéo de Graven - Développement sur les exceptions montre comment gérer plusieurs erreurs.

Regardez ce segment pour voir comment attraper plusieurs exceptions : Attraper plusieurs erreurs : L'auteur montre d'abord comment utiliser deux blocs catch distincts, puis comment les combiner avec l'opérateur |. C'est une technique très courante en développement.


4. Le bloc finally : Le filet de sécurité

Que se passe-t-il si vous avez besoin d'exécuter une action quoi qu'il arrive, qu'une exception ait été levée ou non ? C'est le rôle du bloc finally.

Le code placé dans un bloc finally est garanti d'être exécuté :

  • Après le try, si aucune exception ne s'est produite.
  • Après le catch, si une exception a été attrapée et gérée.
  • Juste avant que le programme ne s'arrête, si une exception non gérée a été levée dans le try.

À quoi ça sert ?
Son utilité principale est de libérer des ressources. En développement backend, vous manipulerez constamment des ressources externes : connexions à des bases de données, fichiers sur le disque, connexions réseau. Ces ressources doivent être fermées proprement pour éviter les fuites de mémoire ou de connexions. Le bloc finally est l'endroit parfait pour s'assurer que databaseConnection.close() ou file.close() soit appelé, même si une erreur a eu lieu pendant leur utilisation.

Apprendre Java #14 Exception (try catch finally)

La vidéo de JirAWS introduit simplement mais efficacement le bloc finally.

Le bloc finally : Ce passage explique la règle d'or : le code dans finally s'exécute systématiquement.

L'article de Zeste de Savoir, que nous avons déjà consulté, renforce cette idée en mentionnant explicitement les cas d'usage professionnels.

Les exceptions - Apprenez à programmer en Java - Zeste de Savoir

Cet article donne le "pourquoi" derrière le finally.

Retournez sur la page de Zeste de Savoir et lisez la partie qui introduit le mot-clé finally (juste après la première section sur try-catch). Retenez bien cette phrase : "Cela est surtout utilisé lorsque vous devez vous assurer d’avoir fermé un fichier, clos votre connexion à une base de données ou un socket". C'est un concept fondamental que nous réutiliserons massivement.


5. Exercice pratique

Mettons tout cela en pratique. Voici une méthode. Sans gestion des exceptions, elle est fragile et peut planter pour deux raisons. Votre mission est de la rendre robuste en utilisant try, catch (multi-catch) et finally.

Code de départ :

public class Parser {

    public static void parseAndPrint(String[] data, int index) {
        // Début du bloc à sécuriser
        String value = data[index];
        int number = Integer.parseInt(value);
        System.out.println("Le nombre est : " + number);
        // Fin du bloc à sécuriser
    }

    public static void main(String[] args) {
        String[] testData = {"10", "20", "trente", "40"};

        // Test 1 : devrait fonctionner
        parseAndPrint(testData, 1);

        // Test 2 : devrait lever NumberFormatException
        parseAndPrint(testData, 2);

        // Test 3 : devrait lever ArrayIndexOutOfBoundsException
        parseAndPrint(testData, 5);
    }
}

Votre objectif :

  1. Modifiez la méthode parseAndPrint pour y ajouter un bloc try-catch-finally.
  2. Le bloc try doit contenir le code existant de la méthode.
  3. Utilisez un seul bloc catch (multi-catch) pour gérer à la fois NumberFormatException et ArrayIndexOutOfBoundsException. Ce bloc doit afficher un message d'erreur générique comme "Erreur de traitement des données."
  4. Le bloc finally doit toujours afficher le message "Fin de l'opération de parsing.".
Cliquez ici pour voir la solution
public class Parser {

    public static void parseAndPrint(String[] data, int index) {
        try {
            String value = data[index];
            int number = Integer.parseInt(value);
            System.out.println("Le nombre est : " + number);
        } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
            // Gère les deux types d'erreurs avec un seul bloc.
            System.out.println("Erreur de traitement des données : " + e.getMessage());
        } finally {
            // Ce bloc est toujours exécuté.
            System.out.println("Fin de l'opération de parsing.");
            System.out.println("-----------------------------");
        }
    }

    public static void main(String[] args) {
        String[] testData = {"10", "20", "trente", "40"};

        System.out.println("Test 1 :");
        parseAndPrint(testData, 1); // Succès

        System.out.println("Test 2 :");
        parseAndPrint(testData, 2); // Erreur de format de nombre

        System.out.println("Test 3 :");
        parseAndPrint(testData, 5); // Erreur d'index
    }
}

Résultat attendu dans la console :

Test 1 :
Le nombre est : 20
Fin de l'opération de parsing.
-----------------------------
Test 2 :
Erreur de traitement des données : For input string: "trente"
Fin de l'opération de parsing.
-----------------------------
Test 3 :
Erreur de traitement des données : Index 5 out of bounds for length 4
Fin de l'opération de parsing.
-----------------------------

Conclusion

Félicitations ! Vous maîtrisez désormais le mécanisme de base de la gestion des exceptions en Java. Vous savez comment empêcher votre programme de s'arrêter brutalement et comment garantir que des actions de nettoyage cruciales soient toujours exécutées.

Les points clés à retenir :

  • Le bloc try isole le code à risque.
  • Le bloc catch intercepte et gère une exception spécifique, permettant au programme de continuer.
  • On peut gérer plusieurs types d'exceptions avec des catch multiples ou un multi-catch (|).
  • Le bloc finally contient le code qui doit toujours s'exécuter, idéalement pour la libération de ressources.

Maintenant que vous savez "attraper" les exceptions, la prochaine leçon explorera l'autre facette de la gestion des erreurs : comment et pourquoi nous voudrions parfois lancer nos propres exceptions ou propager celles que nous recevons. Nous découvrirons également une syntaxe plus moderne, le try-with-resources, qui simplifie grandement la gestion des ressources que nous avons évoquée avec finally.