Java Language La déclaration d'essayer avec les ressources


Exemple

Java SE 7

Comme l'illustre l'exemple de l' instruction try-catch-final , le nettoyage des ressources à l'aide d'une clause finally nécessite une quantité importante de code "chaud-plat" pour implémenter correctement les bordures. Java 7 fournit un moyen beaucoup plus simple de résoudre ce problème sous la forme de l'instruction try-with-resources .

Qu'est ce qu'une ressource?

Java 7 a introduit l'interface java.lang.AutoCloseable pour permettre la gestion des classes à l'aide de l'instruction try-with-resources . Les instances de classes qui implémentent AutoCloseable sont appelées ressources . Celles-ci doivent généralement être éliminées en temps opportun plutôt que de compter sur le ramasse-miettes pour en disposer.

L'interface AutoCloseable définit une méthode unique:

public void close() throws Exception

Une méthode close() doit éliminer la ressource de manière appropriée. La spécification indique qu'il est prudent d'appeler la méthode sur une ressource déjà supprimée. De plus, les classes qui implémentent Autocloseable sont fortement encouragées à déclarer la méthode close() pour générer une exception plus spécifique que Exception , voire aucune exception.

Un large éventail d'interfaces et de classes Java standard implémentent AutoCloseable . Ceux-ci inclus:

  • InputStream , OutputStream et leurs sous-classes
  • Reader , Writer et leurs sous-classes
  • Socket et ServerSocket et leurs sous-classes
  • Channel et ses sous-classes, et
  • les interfaces JDBC Connection , Statement et ResultSet et leurs sous-classes.

Les classes d'application et tierces peuvent également le faire.

L'énoncé de base de try-with-resource

La syntaxe d'un try-with-resources est basée sur des formes classiques de try-catch , try-finally et try-catch-finally . Voici un exemple de formulaire "de base"; c'est à dire la forme sans catch ou finally .

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

Les ressources à gérer sont déclarées comme variables dans la section (...) après la clause try . Dans l'exemple ci-dessus, nous déclarons un stream variable de ressource et l'initialisons à un nouveau PrintStream .

Une fois les variables de ressource initialisées, le bloc try est exécuté. Lorsque cela est terminé, stream.close() sera appelé automatiquement pour garantir que la ressource ne fuit pas. Notez que l'appel close() se produit peu importe la manière dont le bloc se termine.

Les instructions try-with-resource améliorées

L'instruction try-with-resources peut être améliorée avec catch blocs catch et finally , comme avec la syntaxe try-catch-finally pré-Java 7. L'extrait de code suivant ajoute un bloc catch à notre précédent pour gérer l' FileNotFoundException que le constructeur PrintStream peut lancer:

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
} catch (FileNotFoundException ex) {
    System.err.println("Cannot open the file");
} finally {
    System.err.println("All done");
}

Si l'initialisation de la ressource ou le bloc try lève l'exception, le bloc catch sera exécuté. Le bloc finally sera toujours exécuté, comme avec une instruction try-catch-finally classique.

Il y a quelques choses à noter cependant:

  • La variable de ressource est hors de portée dans les blocs catch et finally .
  • Le nettoyage des ressources se produira avant que l'instruction tente de correspondre au bloc catch .
  • Si le nettoyage automatique des ressources a déclenché une exception, cela peut se produire dans l'un des blocs catch .

Gestion de plusieurs ressources

Les extraits de code ci-dessus montrent une seule ressource en cours de gestion. En fait, try-with-resources peut gérer plusieurs ressources dans une seule déclaration. Par exemple:

try (InputStream is = new FileInputStream(file1);
     OutputStream os = new FileOutputStream(file2)) {
    // Copy 'is' to 'os'
}

Cela se comporte comme prévu. Les deux is et os sont fermés automatiquement à la fin de l' try bloc. Il y a quelques points à noter:

  • Les initialisations se produisent dans l'ordre du code, et les initialiseurs de variables de ressource ultérieurs peuvent utiliser les valeurs des précédentes.
  • Toutes les variables de ressources initialisées avec succès seront nettoyées.
  • Les variables de ressource sont nettoyées dans l'ordre inverse de leurs déclarations.

Ainsi, dans l'exemple ci - dessus, is est initialisé avant os et nettoyé après, et is sera nettoyé s'il y a une exception lors de l' initialisation os .

Équivalence d'essais avec ressources et d'essais classiques

La spécification de langage Java spécifie le comportement des formulaires try-with-resource en fonction de l'instruction try-catch-finally classique. (Veuillez vous référer au JLS pour plus de détails.)

Par exemple, ce try-with-resource de base :

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

est défini pour être équivalent à ce try-catch-finally :

// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");

// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;

// The actual try block
try {
    stream.println("Hello world!");
} catch (Throwable t) {
    // If an exception is thrown, remember it for the finally block
    primaryException = t;
    throw t;
} finally {
    if (primaryException == null) {
        // If no exception was thrown so far, exceptions thrown in close() will
        // not be caught and therefore be passed on to the enclosing code.
        stream.close();
    } else {
        // If an exception has already been thrown, any exception thrown in
        // close() will be suppressed as it is likely to be related to the
        // previous exception. The suppressed exception can be retrieved
        // using primaryException.getSuppressed().
        try {
            stream.close();
        } catch (Throwable suppressedException) {
            primaryException.addSuppressed(suppressedException);
        }
    }
}

(Le JLS spécifie que les variables t et primaryException seront invisibles pour le code Java normal.)

La forme améliorée de try-with-resources est spécifiée comme une équivalence avec la forme de base. Par exemple:

try (PrintStream stream = new PrintStream(fileName)) {
    stream.println("Hello world!");
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}

est équivalent à:

try {
    try (PrintStream stream = new PrintStream(fileName)) {
        stream.println("Hello world!");
    }
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}