JavaRush /Blog Java /Random-FR /Pause café #203. Comment gérer les exceptions à l'aide de...

Pause café #203. Comment gérer les exceptions à l'aide de l'instruction try-with-resource

Publié dans le groupe Random-FR
Source : Medium Ce guide décrit les avantages de try-with-resource par rapport à try-catch-finally. Vous apprendrez également dans quelles conditions les exceptions supprimées sont levées et comment utiliser try-with-resources avec plusieurs ressources. Pause café #203.  Comment gérer les exceptions à l'aide de l'instruction try-with-resource - 1La construction try with resources , également connue sous le nom de try-with-resources , est un mécanisme de gestion des exceptions en Java qui peut fermer automatiquement des ressources telles qu'un Java InputStream ou une connexion JDBC lorsqu'il a fini de travailler avec elles. En 2011, Oracle a ajouté try with resources à la syntaxe du langage Java pour garantir que les objets tels que les sockets réseau, les connexions à la base de données et les liens de fichiers et de dossiers sont fermés correctement après leur utilisation. Le fait de ne pas fermer ces ressources après qu'un développeur leur a ouvert un handle peut entraîner des fuites de mémoire, déclenchant des routines de récupération de place évitables et une surcharge du processeur sur le serveur.

Avant Java 7

En Java, si vous utilisez des ressources telles que des flux d'entrée/sortie, vous devez toujours les fermer après utilisation. Puisqu'ils peuvent également lever des exceptions, ils doivent être contenus dans un bloc try-catch . La fermeture doit avoir lieu dans un bloc final . C'était du moins le cas jusqu'à Java 7. Mais cela présente plusieurs inconvénients :
  • Vous devez vérifier si votre ressource est nulle avant de la fermer.
  • La fermeture elle-même peut lever des exceptions, vous devez donc avoir un autre try-catch dans votre bloc final .
  • Les programmeurs ont tendance à oublier de fermer leurs ressources.

Comment utiliser l'instruction try-with-resource ?

Cet opérateur a été initialement introduit dans Java 7 et l'idée était que les développeurs n'avaient plus à se soucier de la gestion des ressources qu'ils utilisent dans un bloc try-catch-finally . Ceci est réalisé en éliminant le besoin de blocs final , qui, en pratique, n'étaient utilisés que par les développeurs pour fermer les ressources. En Java, une instruction try-with-resources est une instruction try qui déclare une ou plusieurs ressources. Une ressource est un objet qui doit être fermé une fois le programme terminé. Lorsque l'exécution du code quitte le bloc try-with-resources , toute ressource ouverte dans le bloc try-with-resources est automatiquement fermée, que des exceptions soient levées soit dans le bloc try-with-resources , soit lors de la tentative de fermeture des ressources. . Pour utiliser la fonctionnalité du langage Java try-with-resources , les règles suivantes s'appliquent :
  • Tous les objets contrôlés par l' instruction try-with-resources doivent implémenter l' interface AutoCloseable .
  • Plusieurs objets AutoCloseable peuvent être créés dans un bloc try-with-resources .
  • Les objets déclarés dans une instruction try-with-resources sont exécutés dans un bloc try , mais pas dans des blocs catch ou enfin .
  • La méthode close() des objets déclarés dans un bloc try-with-resources est appelée qu'une exception soit levée ou non au moment de l'exécution.
  • Si une exception est levée dans la méthode close() , elle peut être classée comme exception supprimée.
Les blocs catch et enfin peuvent toujours être utilisés dans un bloc try-with-resource , et ils fonctionneront de la même manière que dans un bloc try normal . Une ressource référencée par un objet AutoCloseable sera toujours fermée si try-with-resource est utilisé . Cela élimine les fuites de mémoire potentielles, généralement causées par une mauvaise allocation des ressources.

Syntaxe

try(declare resources here) {
    // использовать ресурсы
}
catch(FileNotFoundException e) {
    // обработка исключений
}

Utilisation pratique de try-with-resource

Pour se fermer automatiquement, la ressource doit être déclarée et initialisée dans try :
try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
    writer.println("Hello World");
}

Remplacer try-catch-finally par try-with-resources

Un moyen simple et évident d'utiliser la fonctionnalité try-with-resources consiste à remplacer le bloc try-catch-finally traditionnel et verbeux . Comparons les exemples de code suivants. Le premier exemple est un bloc try-catch-finally typique :
Scanner scanner = null;
try {
    scanner = new Scanner(new File("test.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}
Et voici une nouvelle solution super concise utilisant try-with-resources :
try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

Différence entre essayer et essayer avec ressource

En ce qui concerne les exceptions, il existe une différence entre un bloc try-catch-finally et un bloc try-with-resources . L'exception est levée à la fois dans le bloc try et final , mais la méthode renvoie l'exception levée uniquement dans le bloc final . Pour try-with-resources , si une exception se produit dans le bloc try et dans l'instruction try-with-resources , la méthode renvoie l'exception levée dans le bloc try . Les exceptions levées par le bloc try-with-resources sont supprimées, c'est-à-dire que nous pouvons dire que le bloc try-with-resources renvoie des exceptions supprimées.

Pourquoi devrais-je utiliser l’instruction try-with-resource ?

L' instruction try-with-resources garantit que chaque ressource est fermée à la fin de l'instruction. Si nous ne fermons pas les ressources, cela peut entraîner une fuite des ressources et le programme peut épuiser les ressources dont il dispose. Cela se produit lorsque vous utilisez un bloc try-catch-finally . Avant Java SE 7, vous pouviez utiliser un bloc final pour garantir que la ressource serait fermée, que l' instruction try se termine normalement ou brusquement. L'exemple suivant utilise un bloc final au lieu d'une instruction try-with-resources :
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {

    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr);
    try {
        return br.readLine();
    } finally {
        br.close();
        fr.close();
    }
}
Il peut y avoir une fuite de ressources dans cet exemple. Le programme doit faire plus que compter sur le garbage collector pour libérer la mémoire de la ressource une fois qu'il a fini de l'utiliser. Le programme doit également renvoyer la ressource au système d'exploitation, généralement en appelant la méthode close de la ressource. Cependant, si le programme ne le fait pas avant que le ramasse-miettes ne renvoie la ressource, les informations nécessaires pour libérer la ressource seront perdues. Une ressource a été divulguée et le système d'exploitation considère qu'elle est toujours utilisée. Dans l'exemple ci-dessus, si la méthode readLine lève une exception et que l' instruction br.close() dans le bloc final lève une exception, alors FileReader a fui . C'est pourquoi il est préférable d'utiliser une instruction try-with-resources plutôt qu'un bloc final pour fermer les ressources de votre programme.

Encore un exemple

L'exemple suivant lit la première ligne d'un fichier. Les instances FileReader et BufferedReader sont utilisées pour lire les données . Ce sont des ressources qui doivent être fermées après la fermeture du programme.
import java.io.*;
class Main {
  public static void main(String[] args) {
    String line;
    try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    }
  }
}
Comme vous pouvez le voir, la ressource déclarée dans l' instruction try-with-resources est un BufferedReader . Les instructions de déclaration de cette ressource apparaissent entre parenthèses immédiatement après le mot clé try . Les classes BufferedReader dans Java SE 7 et versions ultérieures implémentent l' interface java.lang.AutoCloseable . Étant donné que les instances BufferedReader sont déclarées dans une instruction try-with-resource , elles seront fermées, que l' instruction try se termine normalement ou brusquement (si la méthode BufferedReader.readLine() renvoie une IOException ).

Exceptions supprimées

Si un bloc try lève une exception et qu'une ou plusieurs exceptions apparaissent dans le bloc try-with-resources , alors les exceptions levées par le bloc try-with-resources sont supprimées. En d’autres termes, nous pouvons dire que les exceptions levées par try-with-resources sont des exceptions supprimées. Vous pouvez intercepter ces exceptions en utilisant la méthode getSuppress() de la classe Throwable . Dans l'exemple présenté précédemment, des exceptions ont été levées par l' instruction try-with-resources , dans les conditions suivantes :
  • Le fichier test.txt n'a pas été trouvé.
  • Fermeture de l' objet BufferedReader .
Une exception peut également être levée à partir d'un bloc try , car la lecture d'un fichier peut échouer à tout moment pour de nombreuses raisons. Si des exceptions sont levées à la fois à partir d'un bloc try et d' une instruction try-with-resources , alors dans le premier cas, une exception est levée et dans le second cas, l'exception est supprimée.

Obtenir des exceptions supprimées

Dans Java 7 et versions ultérieures, les exceptions supprimées peuvent être obtenues en appelant la méthode Throwable.getSuppressed() à partir d'une exception levée par un bloc try . getSuppress() renvoie un tableau contenant toutes les exceptions supprimées par l' instruction try-with-resources . Si aucune exception n'est supprimée ou si la suppression est désactivée, un tableau vide est renvoyé. Voici un exemple de réception d'exceptions supprimées dans un bloc catch :
catch(IOException e) {
  System.out.println("Thrown exception=>" + e.getMessage());
  Throwable[] suppressedExceptions = e.getSuppressed();
  for (int i=0; i" + suppressedExceptions[i]);
  }
}

Avantages de l'utilisation de try-with-resources

  • Code lisible et facile à écrire.
  • Gestion automatique des ressources.
  • Le nombre de lignes de code a été réduit.
  • Lorsque plusieurs ressources sont ouvertes dans try-with-resources , elles sont fermées dans l'ordre inverse pour éviter les problèmes de dépendance.
Et bien sûr, un bloc final n’est plus nécessaire pour fermer une ressource . Auparavant, avant Java 7, nous devions utiliser un bloc final pour garantir qu'une ressource est fermée afin d'éviter les fuites de ressources. Voici un programme similaire au tout premier exemple. Dans ce programme, nous avons utilisé un bloc final pour fermer les ressources.
import java.io.*;
class Main {
  public static void main(String[] args) {
    BufferedReader br = null;
    String line;
    try {
      System.out.println("Entering try block");
      br = new BufferedReader(new FileReader("test.txt"));
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    } finally {
      System.out.println("Entering finally block");
      try {
        if (br != null) {
          br.close();
        }
      } catch (IOException e) {
        System.out.println("IOException in finally block =>"+e.getMessage());
      }
    }
  }
}
Conclusion:
Saisie du bloc try Line => ligne du fichier test.txt Saisie du bloc final
Comme vous pouvez le voir dans l'exemple ci-dessus, l'utilisation d'un bloc final pour nettoyer les ressources ajoute de la complexité au code. Remarquez le bloc try...catch dans le bloc enfin ? En effet, une IOException peut également se produire lors de la fermeture d'une instance BufferedReader à l'intérieur d'un bloc final , elle est donc également interceptée et gérée. L' instruction try-with-resources effectue une gestion automatique des ressources. Nous n'avons pas besoin de fermer explicitement les ressources car la JVM les ferme automatiquement. Cela rend le code plus lisible et plus facile à écrire.

Essayer avec des ressources avec plusieurs ressources

Nous pouvons déclarer plusieurs ressources dans un bloc try-with-resources en les séparant simplement par un point-virgule :
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    while (scanner.hasNext()) {
	writer.print(scanner.nextLine());
    }
}

Java 9 - Variables effectivement finales

Avant Java 9, nous ne pouvions utiliser de nouvelles variables qu'à l'intérieur d'un bloc try-with-resources :
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    // omitted
}
Notez que ceci est assez verbeux lors de la déclaration de plusieurs ressources. Depuis Java 9 (mise à jour JEP 213), nous pouvons utiliser des variables final ou même effectivement final à l'intérieur d'un bloc try-with-resources :
final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) {
    // omitted
}
En termes simples, une variable est effectivement final si elle n'est pas modifiée après la première affectation, même si elle n'est pas explicitement marquée final . Comme indiqué ci-dessus, la variable scanner est explicitement déclarée final afin que nous puissions l'utiliser avec un bloc try-with-resources . Bien que la variable write ne soit pas explicitement final , elle ne change pas après la première affectation. Nous pouvons donc également utiliser la variable write . J'espère qu'aujourd'hui vous comprenez mieux comment gérer les exceptions à l'aide de l'instruction try-with-resource . Bon apprentissage!
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION