Différence entre try, catch et throw en Java

Quatre mots-clés forment le socle de la gestion d'erreurs en Java : try, catch, throw et throws. Ils ont des rôles très différents mais sont souvent confondus. Ce guide les clarifie un par un.

try : délimiter une zone à risque

Le bloc try entoure du code susceptible de lever une exception :

try {
    int resultat = 10 / 0; // lève ArithmeticException
    System.out.println(resultat);
}

Un try doit obligatoirement être suivi d'au moins un catch ou un finally, sinon le compilateur refuse.

catch : intercepter une exception

Le bloc catch capture une exception lancée dans le try correspondant :

try {
    int resultat = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Division par zéro : " + e.getMessage());
}

On peut chaîner plusieurs catch du plus spécifique au plus général :

try {
    lireFichier("/etc/config");
} catch (FileNotFoundException e) {
    System.out.println("Fichier absent");
} catch (IOException e) {
    System.out.println("Erreur I/O : " + e.getMessage());
} catch (Exception e) {
    System.out.println("Erreur inattendue");
}

Depuis Java 7, le multi-catch permet de traiter plusieurs types dans un seul bloc :

try {
    ...
} catch (IOException | SQLException e) {
    logger.error("Erreur I/O ou base de données", e);
}

throw : lancer une exception

Le mot-clé throw sert à émettre une exception manuellement :

public void retirer(int montant) {
    if (montant <= 0) {
        throw new IllegalArgumentException("Le montant doit être positif");
    }
    if (montant > solde) {
        throw new IllegalStateException("Solde insuffisant");
    }
    solde -= montant;
}

Usage typique : validation des paramètres d'entrée, invariants métier, cas impossibles.

throws : déclarer les exceptions propagées

Contrairement à throw, throws apparaît dans la signature de méthode. Il annonce les exceptions vérifiées que la méthode peut propager à son appelant :

public String lireFichier(String chemin) throws IOException {
    return Files.readString(Paths.get(chemin));
}

Quiconque appelle lireFichier devra soit l'entourer d'un try/catch, soit déclarer lui aussi throws IOException.

Différence clé : checked vs unchecked

TypeClasse racinethrows obligatoire ?Exemple
CheckedExceptionOuiIOException, SQLException
UncheckedRuntimeExceptionNonNullPointerException, IllegalArgumentException
ErrorErrorNonOutOfMemoryError

Les checked exceptions forcent le compilateur à exiger un traitement. Les unchecked sont habituellement le signe d'un bug ou d'un état invalide et ne sont pas captives du throws.

Exemple complet

public class Compte {
    private double solde;

    public void retirer(double montant) throws SoldeInsuffisantException {
        if (montant <= 0) {
            throw new IllegalArgumentException("Montant invalide");
        }
        if (montant > solde) {
            throw new SoldeInsuffisantException("Solde : " + solde);
        }
        solde -= montant;
    }
}

public static void main(String[] args) {
    Compte c = new Compte();
    try {
        c.retirer(100);
    } catch (SoldeInsuffisantException e) {
        System.err.println("Retrait refusé : " + e.getMessage());
    } catch (IllegalArgumentException e) {
        System.err.println("Paramètre invalide : " + e.getMessage());
    }
}

finally : toujours exécuté

Un bloc finally s'exécute que l'exception soit levée ou non, très utile pour fermer des ressources :

FileInputStream in = null;
try {
    in = new FileInputStream("data.txt");
    // ...
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (in != null) try { in.close(); } catch (IOException ignore) {}
}

Cette forme est aujourd'hui dépassée : préférez try-with-resources qui appelle close() automatiquement :

try (FileInputStream in = new FileInputStream("data.txt")) {
    // ...
} catch (IOException e) {
    e.printStackTrace();
}

Bonnes pratiques

  • Ne jamais attraper Throwable : cela inclut OutOfMemoryError qu'on ne peut rarement gérer.
  • Ne pas attraper si on ne sait pas quoi faire — laissez l'exception remonter jusqu'à un endroit qui sait réagir.
  • Conserver la cause quand vous relancez : throw new MonException("msg", e);
  • Préférer les RuntimeException pour les erreurs programmeur (argument invalide, état impossible).
  • Utiliser try-with-resources pour tout objet qui implémente AutoCloseable.

Résumé

  • try : délimite une zone où une exception peut survenir.
  • catch : intercepte et traite une exception.
  • throw : lance manuellement une exception.
  • throws : déclare les exceptions vérifiées qu'une méthode peut laisser remonter.

Ces quatre mots-clés forment la base ; combinés correctement, ils produisent un code Java robuste, lisible et clair sur sa gestion des erreurs.