FRFAM.COM >> Famille >> Technologie &Innovation >> Informatique

Gérez-vous correctement les exceptions en Java ? Guide expert

Une exception en programmation désigne une condition exceptionnelle survenant lors de l'exécution d'un programme. Elle est utilisée lorsque cette condition peut être mieux gérée ailleurs qu'à l'endroit où elle se produit. Voici quelques exemples :

  • L'échec d'ouverture d'un fichier de configuration peut être géré plus haut dans le code, par exemple en utilisant un autre emplacement.
  • Accéder à un élément de tableau hors limites indique un bug : déboguez !
  • Une erreur d'analyse XML doit être signalée à l'utilisateur pour corriger le fichier.
  • Un manque de mémoire (traitement de fichier volumineux) peut être résolu en augmentant la mémoire allouée au processus Java.

Dans ces cas, l'exception doit être traitée en dehors de son point de génération pour corriger la cause sous-jacente.

Types d'exceptions

L'image ci-dessous illustre la hiérarchie principale des exceptions Java. La classe de base est Throwable, sous-classée en Exception et Error. Exception concerne les conditions récupérables par l'application. Error signale des erreurs graves de l'environnement JVM, comme OutOfMemoryError ou StackOverflowError, que l'application ne doit pas intercepter.

Gérez-vous correctement les exceptions en Java ? Guide expert

Les exceptions se divisent en vérifiées (checked) et non vérifiées (unchecked). Une exception vérifiée doit être gérée par le code appelant ou déclarée ; le compilateur l'exige. Une non vérifiée (sous-classe de RuntimeException) peut propager librement.

Exceptions vérifiées

La méthode suivante tente de créer un FileReader. Son constructeur lève FileNotFoundException (vérifiée), qui doit être gérée ou déclarée.

Ce code ne compile pas :

private void loadFile(String filename) {
  FileReader in = new FileReader(filename);
}

Pour compiler, gérez-la :

private void loadFile(String filename) {
  try {
    FileReader in = new FileReader(filename);
  } catch (FileNotFoundException ex) {
    // Gérer l'exception ici
  }
}

Ou déclarez-la :

private void loadFile(String filename) throws java.io.FileNotFoundException {
  FileReader in = new FileReader(filename);
}

Exceptions non vérifiées

Une non vérifiée, sous-classe de RuntimeException, n'exige pas de gestion explicite. Exemple générant NullPointerException :

private void handleEvent() {
  String name = null;
  if (name.length() > 0) {
  }
}

Encapsulation d'exceptions

Pour simplifier, encapsulez une vérifiée dans une non vérifiée.

method_1() lève SQLException :

private void method_1() throws SQLException {
  // ...
  throw new SQLException();
}

Dans method_2() :

private void method_2() {
  try {
    method_1();
  } catch (SQLException ex) {
    throw new RuntimeException(ex);
  }
}

Trace de pile d'exception

La trace de pile liste les appels actifs capturés par la JVM, avec classe, méthode, fichier et ligne. Utile pour déboguer.

Exemple :

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 8, Size: 5
	at java.util.ArrayList.rangeCheck(ArrayList.java:653)
	at java.util.ArrayList.get(ArrayList.java:429)
	at sample.sample1.main(sample1.java:24)

Gestion des exceptions

Utilisez try-catch pour corriger. L'objet exception fournit des infos via ses méthodes.

Exemple de logging :

private void loadConfig() {
  try {
    // Code pouvant lever IOException
  } catch (IOException ex) {
    log.warning(ex.getMessage());
  }
}

Récupérer cause encapsulée :

Throwable cause = ex.getCause();
log.warning("Cause : " + cause.getMessage());

Trace de pile :

StringBuilder sbuf = new StringBuilder("Stack Trace:");
for (StackTraceElement el : ex.getStackTrace()) {
  sbuf.append(el.getClassName() + "." + el.getMethodName()).append("\n");
}
log.warning(sbuf.toString());

Logger et relancer :

try {
  // ...
} catch (IOException ex) {
  log.warning(ex.getMessage());
  throw ex;
}

Imprimer trace :

try {
  // ...
} catch (IOException ex) {
  PrintStream out = ...;
  out.println(ex.getMessage());
  ex.printStackTrace(out);
}

Multi-exceptions :

try {
  // ...
} catch (IOException ex) {
  // IO
} catch (SQLException ex) {
  // SQL
}

Ou multi-catch (Java 7+) :

try {
  // ...
} catch (IOException | SQLException ex) {
  // Commun
} catch (SAXException ex) {
  // SAX
}

Nettoyage avec finally

Nettoyez les ressources (fichiers, connexions) dans finally :

InputStream in = null;
try {
  in = new FileInputStream(filename);
  // ...
} catch (IOException ex) {
  log.warning(ex.getMessage());
} finally {
  if (in != null) in.close();
}

Try-with-resources

Depuis Java 7, simplifie le nettoyage pour AutoCloseable :

try (InputStream in = new FileInputStream("..")) {
  // Utilisation
}

Multi-ressources :

try (InputStream in = new FileInputStream("..");
     Connection con = ...) {
  // ...
}

Classe exemple :

public class MyClass implements AutoCloseable {
  public void close() {
    // Nettoyage
  }
}
try (MyClass obj = new MyClass(..)) {
  // ...
}

Exceptions courantes

  • IndexOutOfBoundsException (unchecked) : Index hors limites.
  • SQLException (checked) : Erreur base de données.
  • IOException (checked) : Erreur I/O.
  • InterruptedException (checked) : Thread interrompu.
  • SAXException (checked) : Erreur parsing XML.
  • NullPointerException (unchecked) : Utilisation de null.

Conclusion

Les exceptions sont essentielles pour signaler et gérer les erreurs en Java. Une bonne utilisation améliore la robustesse du code en production.

Avez-vous des anecdotes sur les exceptions ? Partagez en commentaires !

Crédit image : Dmitry Nikolaev via Shutterstock.com

[]