La suppression d'exceptions en Java : maitriser try-with-resources et la gestion des ressources

Decouvrez comment gerer efficacement les exceptions supprimees en Java avec try-with-resources, comprendre le mecanisme de suppression et eviter les pieges courants de la gestion des ressources.

Mahmoud DEVO
Mahmoud DEVO
December 27, 2025 7 min read
La suppression d'exceptions en Java : maitriser try-with-resources et la gestion des ressources

Introduction

La gestion des ressources et la suppression d’exceptions sont deux concepts essentiels pour les developpeurs Java. Avant Java 7, la fermeture correcte des ressources (fichiers, connexions reseau, flux de donnees) etait une source frequente de bugs et de fuites de memoire. Le code etait verbeux, complexe et prone aux erreurs.

Avec l’introduction du constructeur try-with-resources dans Java 7, la gestion des ressources est devenue beaucoup plus simple et sure. Mais un aspect souvent meconnu de ce mecanisme est la suppression d’exceptions (exception suppression) : que se passe-t-il lorsque plusieurs exceptions sont levees simultanement ?

Dans ce guide complet, nous allons explorer :

  • Le mecanisme try-with-resources et son fonctionnement interne
  • Le concept de suppression d’exceptions et comment y acceder
  • La comparaison avec l’approche classique try-catch-finally
  • Les bonnes pratiques et les pieges a eviter

Que vous soyez un developpeur Java debutant ou experimente, comprendre ces mecanismes vous aidera a ecrire du code plus robuste et maintenable.

Le Constructeur try-with-resources

Le constructeur try-with-resources a ete introduit dans Java 7 pour simplifier la gestion des ressources. Il permet aux developpeurs de definir plusieurs ressources dans une seule structure try, ce qui est particulierement utile lorsqu’il faut gerer plusieurs ressources au cours d’une operation.

Syntaxe de base

Voici un exemple simple :

try (Writer w = new BufferedWriter(new FileWriter(someFilename))) {
    w.write("Hello, World!");
    // La ressource sera automatiquement fermee
}

Dans cet exemple, le constructeur try-with-resources assure que la ressource w est fermee automatiquement, meme si une exception est levee.

Gestion de plusieurs ressources

Vous pouvez declarer plusieurs ressources separees par des points-virgules :

try (FileInputStream fis = new FileInputStream("input.txt");
     FileOutputStream fos = new FileOutputStream("output.txt");
     BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {

    String line;
    while ((line = reader.readLine()) != null) {
        fos.write(line.getBytes());
        fos.write('\n');
    }
}
// Toutes les ressources sont fermees dans l'ordre inverse de leur declaration

L’interface AutoCloseable

Pour qu’une ressource puisse etre utilisee avec try-with-resources, elle doit implementer l’interface AutoCloseable :

public class MaRessource implements AutoCloseable {
    private String nom;

    public MaRessource(String nom) {
        this.nom = nom;
        System.out.println("Ressource " + nom + " ouverte");
    }

    public void utiliser() {
        System.out.println("Utilisation de " + nom);
    }

    @Override
    public void close() throws Exception {
        System.out.println("Ressource " + nom + " fermee");
    }
}

// Utilisation
try (MaRessource r1 = new MaRessource("R1");
     MaRessource r2 = new MaRessource("R2")) {
    r1.utiliser();
    r2.utiliser();
}
// Sortie :
// Ressource R1 ouverte
// Ressource R2 ouverte
// Utilisation de R1
// Utilisation de R2
// Ressource R2 fermee
// Ressource R1 fermee

Le Concept de Suppression d’Exceptions

Lorsqu’une exception est levee pendant l’execution du bloc try, le constructeur try-with-resources assure que toutes les ressources sont fermees avant de lever l’exception. Mais que se passe-t-il si une seconde exception est levee pendant la fermeture des ressources ?

C’est la que le mecanisme de suppression d’exceptions entre en jeu. L’exception levee lors de la fermeture sera “supprimee” et attachee a l’exception principale.

Exemple concret

public class RessourceDefaillante implements AutoCloseable {
    private String nom;

    public RessourceDefaillante(String nom) {
        this.nom = nom;
    }

    public void operation() {
        throw new RuntimeException("Erreur dans operation() de " + nom);
    }

    @Override
    public void close() {
        throw new RuntimeException("Erreur dans close() de " + nom);
    }
}

// Test du mecanisme de suppression
try (RessourceDefaillante r = new RessourceDefaillante("R1")) {
    r.operation(); // Leve une exception
} catch (RuntimeException e) {
    System.out.println("Exception principale : " + e.getMessage());

    // Recuperer les exceptions supprimees
    Throwable[] supprimees = e.getSuppressed();
    for (Throwable t : supprimees) {
        System.out.println("Exception supprimee : " + t.getMessage());
    }
}
// Sortie :
// Exception principale : Erreur dans operation() de R1
// Exception supprimee : Erreur dans close() de R1

Acces aux exceptions supprimees

La methode getSuppressed() de la classe Throwable permet de recuperer toutes les exceptions supprimees :

try {
    try (RessourceDefaillante r1 = new RessourceDefaillante("R1");
         RessourceDefaillante r2 = new RessourceDefaillante("R2")) {
        r1.operation();
    }
} catch (Exception e) {
    System.out.println("Exception principale : " + e.getMessage());

    Throwable[] supprimees = e.getSuppressed();
    System.out.println("Nombre d'exceptions supprimees : " + supprimees.length);

    for (int i = 0; i < supprimees.length; i++) {
        System.out.println("  [" + i + "] " + supprimees[i].getMessage());
    }
}

Ajout manuel d’exceptions supprimees

Vous pouvez egalement ajouter manuellement des exceptions supprimees avec addSuppressed() :

Exception principale = new Exception("Erreur principale");
principale.addSuppressed(new Exception("Erreur secondaire 1"));
principale.addSuppressed(new Exception("Erreur secondaire 2"));

throw principale;

Comparaison : try-with-resources vs try-catch-finally

L’approche classique avec try-catch-finally

Avant Java 7, la gestion des ressources se faisait avec le bloc finally :

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("fichier.txt"));
    String ligne = reader.readLine();
    // Traitement...
} catch (IOException e) {
    System.err.println("Erreur de lecture : " + e.getMessage());
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            // Que faire ici ? L'exception de fermeture est perdue !
            System.err.println("Erreur de fermeture ignoree");
        }
    }
}

Le probleme de la perte d’exceptions

Avec l’approche classique, si une exception est levee dans le bloc try ET une autre dans le bloc finally, l’exception du finally ecrase celle du try :

// PROBLEME : l'exception originale est perdue !
try {
    throw new Exception("Exception importante du try");
} finally {
    throw new Exception("Exception du finally"); // Celle-ci est propagee
}

Solution avec try-with-resources

Le mecanisme de suppression resout ce probleme :

try (MaRessource r = new MaRessource()) {
    throw new Exception("Exception importante du try");
} // Si close() leve une exception, elle sera supprimee, pas perdue

Tableau comparatif

Aspecttry-catch-finallytry-with-resources
Fermeture automatiqueNonOui
Ordre de fermetureManuelInverse de declaration
Exceptions multiplesPerte possibleSuppression
LisibiliteVerbeuxConcis
Null-safetyVerification manuelleAutomatique

Bonnes Pratiques

1. Toujours preferer try-with-resources

// BON : try-with-resources
try (Connection conn = dataSource.getConnection();
     PreparedStatement stmt = conn.prepareStatement(sql);
     ResultSet rs = stmt.executeQuery()) {
    while (rs.next()) {
        // Traitement
    }
}

// MAUVAIS : try-catch-finally verbeux et error-prone
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = dataSource.getConnection();
    stmt = conn.prepareStatement(sql);
    rs = stmt.executeQuery();
    // ...
} finally {
    // Fermeture manuelle penible
}

2. Logger les exceptions supprimees

try (MaRessource r = new MaRessource()) {
    r.operation();
} catch (Exception e) {
    logger.error("Erreur principale : {}", e.getMessage(), e);

    // Ne pas oublier les exceptions supprimees !
    for (Throwable suppressed : e.getSuppressed()) {
        logger.warn("Exception supprimee : {}", suppressed.getMessage(), suppressed);
    }
}

3. Implementer close() de maniere idempotente

public class RessourceSure implements AutoCloseable {
    private boolean fermee = false;

    @Override
    public void close() {
        if (!fermee) {
            fermee = true;
            // Logique de fermeture
        }
        // Appels multiples a close() sont sans effet
    }
}

4. Utiliser les ressources Java 9+ deja declarees

A partir de Java 9, vous pouvez utiliser des variables effectivement finales :

BufferedReader reader = new BufferedReader(new FileReader("fichier.txt"));
try (reader) {  // Java 9+
    // Utilisation
}

Pieges Courants

1. Oublier de verifier getSuppressed()

// PIEGE : Les exceptions supprimees sont ignorees
try (RessourceDefaillante r = new RessourceDefaillante()) {
    r.operation();
} catch (Exception e) {
    // MAUVAIS : On ne voit que l'exception principale
    System.err.println("Erreur : " + e.getMessage());

    // SOLUTION : Toujours verifier les exceptions supprimees
    if (e.getSuppressed().length > 0) {
        System.err.println("Attention : " + e.getSuppressed().length +
                           " exception(s) supprimee(s)");
    }
}

2. Ordre de fermeture inattendu

// Les ressources sont fermees dans l'ORDRE INVERSE
try (Ressource r1 = new Ressource("Premier");   // Ferme en dernier
     Ressource r2 = new Ressource("Deuxieme");  // Ferme en deuxieme
     Ressource r3 = new Ressource("Troisieme")) { // Ferme en premier
    // ...
}
// Ordre de fermeture : r3 -> r2 -> r1

3. Ressources dependantes mal ordonnees

// PIEGE : Si r2 depend de r1, r1 sera ferme apres r2
try (Connection conn = getConnection();
     Statement stmt = conn.createStatement()) {  // OK : stmt depend de conn
    // stmt sera ferme avant conn
}

// MAUVAIS : Ordre inverse
try (Statement stmt = conn.createStatement();  // ERREUR potentielle
     Connection conn = getConnection()) {
    // conn sera ferme avant stmt !
}

4. Exception dans le constructeur de ressource

try (Ressource r1 = new Ressource();      // OK
     Ressource r2 = new RessourceQuiEchoue()) {  // Leve exception
    // Ce bloc n'est jamais execute
} // r1 sera quand meme fermee correctement !

Conclusion

La gestion des ressources et la suppression d’exceptions sont des mecanismes essentiels pour ecrire du code Java robuste et maintenable.

Points cles a retenir :

  1. Utilisez toujours try-with-resources pour gerer les ressources qui implementent AutoCloseable
  2. Comprenez le mecanisme de suppression : l’exception principale est preservee, les exceptions de fermeture sont ajoutees via addSuppressed()
  3. Verifiez systematiquement getSuppressed() lors du traitement des exceptions pour ne pas perdre d’informations de diagnostic
  4. L’ordre de declaration compte : les ressources sont fermees dans l’ordre inverse de leur declaration
  5. Implementez close() de maniere idempotente pour eviter les problemes lors d’appels multiples

En adoptant ces bonnes pratiques, vous eviterez les fuites de ressources et les bugs difficiles a diagnostiquer lies a la perte d’exceptions.

Ressources supplementaires

Advertisement

In-Article Ad

Dev Mode

Share this article

Mahmoud DEVO

Mahmoud DEVO

Senior Full-Stack Developer

I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.

Enjoyed this article?

Subscribe to get more tech content delivered to your inbox.

Related Articles