web-dev-qa-db-fra.com

Fermez plusieurs ressources avec AutoCloseable (try-with-resources)

Je sais que la ressource que vous transmettez avec un essai sera fermée automatiquement si la ressource est dotée d'AutoCloseable. Jusqu'ici tout va bien. Mais qu'est-ce que je fais quand j'ai plusieurs ressources que je veux fermer automatiquement? Exemple avec des sockets;

try (Socket socket = new Socket()) {
    input = new DataInputStream(socket.getInputStream());
    output = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
} 

Je sais donc que le socket sera correctement fermé, car il est passé comme paramètre lors de l’essai, mais comment l’entrée et la sortie doivent-elles être fermées correctement?

53
asmb

Essayez avec des ressources peut être utilisé avec plusieurs ressources en les déclarant tous entre parenthèses. Voir le documentation

Extrait du code pertinent de la documentation liée:

public static void writeToFileZipFileContents(String zipFileName,
                                           String outputFileName)
                                           throws Java.io.IOException {

    Java.nio.charset.Charset charset =
         Java.nio.charset.StandardCharsets.US_ASCII;
    Java.nio.file.Path outputFilePath =
         Java.nio.file.Paths.get(outputFileName);

    // Open Zip file and create output file with 
    // try-with-resources statement

    try (
        Java.util.Zip.ZipFile zf =
             new Java.util.Zip.ZipFile(zipFileName);
        Java.io.BufferedWriter writer = 
            Java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        // Enumerate each entry
        for (Java.util.Enumeration entries =
                                zf.entries();     entries.hasMoreElements();) {
            // Get the entry name and write it to the output file
            String newLine = System.getProperty("line.separator");
            String zipEntryName =
                 ((Java.util.Zip.ZipEntry)entries.nextElement()).getName() 
             newLine;
            writer.write(zipEntryName, 0, zipEntryName.length());
        }
    }
}

Si vos objets n'implémentent pas AutoClosable (DataInputStream ne), ou doivent être déclarés avant les ressources try-with-resource, l'emplacement approprié pour les fermer est dans un finally block, également mentionné dans la documentation liée.

67
augray

Ne vous inquiétez pas, les choses vont "fonctionner". De documentation de Socket :

La fermeture de ce socket fermera également les entrées InputStream et OutputStream du socket.

Je comprends votre souci de ne pas appeler explicitement close() sur les objets d'entrée et de sortie et, en fait, il est généralement préférable de s'assurer que toutes les ressources sont gérées automatiquement par le bloc try-with-resources, Comme ceci:

try (Socket socket = new Socket();
     InputStream input = new DataInputStream(socket.getInputStream());
     OutputStream output = new DataOutputStream(socket.getOutputStream());) {
} catch (IOException e) {
} 

Cela aurait pour effet que l'objet socket serait "fermé plusieurs fois", mais cela ne devrait pas causer de préjudice (c'est l'une des raisons pour lesquelles il est généralement conseillé de rendre idempotentes toutes les implémentations de close()) .

21
Paulo

Outre les réponses ci-dessus, il s'agit de l'amélioration ajoutée à Java 9.

Les ressources d’essai avec ressources Java 9 améliorent l’écriture du code. Maintenant, vous pouvez déclarer la variable en dehors du bloc try et les utiliser à l'intérieur du bloc try directement. En effet, vous bénéficierez des avantages suivants.

  • Les ressources déclarées en dehors de try (qui est effectivement final ou final) peuvent être automatiquement fermées par la gestion automatique des ressources en les ajoutant simplement dans le bloc try.
    • Vous n'avez pas besoin de refaire référence à des objets déclarés en dehors du bloc try ni de les fermer manuellement, comme nous devons le faire dans Java 7.
    • Cela aide aussi à écrire du code propre.

try-with-resource peut-on écrire comme ceci dans Java 9.

public void loadDataFromDB() throws SQLException {
Connection dbCon = DriverManager.getConnection("url", "user", "password");
try (dbCon; ResultSet rs = dbCon.createStatement().executeQuery("select * from emp")) {
    while (rs.next()) {
        System.out.println("In loadDataFromDB() =====>>>>>>>>>>>> " + rs.getString(1));
    }
} catch (SQLException e) {
    System.out.println("Exception occurs while reading the data from DB ->" + e.getMessage());
}

}

Ici, la gestion automatique des ressources fermera automatiquement les deux objets dbCon & rs.

Pour une meilleure compréhension de la liste des cas d’utilisation définis ci-dessus, veuillez trouver du code Java 7.

Exemple 1:

public void loadDataFromDB() throws SQLException {
Connection dbCon = DriverManager.getConnection("url", "user", "password");
try (ResultSet rs = dbCon.createStatement().executeQuery("select * from emp")) {
    while (rs.next()) {
        System.out.println("In loadDataFromDB() =====>>>>>>>>>>>> " + rs.getString(1));
    }
} catch (SQLException e) {
    System.out.println("Exception occurs while reading the data from DB ->" + e.getMessage());
} finally {
    if (null != dbCon)
        dbCon.close();
}

}

Exemple 2:

// BufferedReader is declared outside try() block
    BufferedReader br = new BufferedReader(new FileReader("C://readfile/input.txt"));

    try (BufferedReader inBr = br) {
            // ...
        }
    } catch (IOException e) {
        // ...
    }

Dans les exemples ci-dessus, vous pouvez voir si l'objet est hors essai, nous devons soit le fermer manuellement, soit le renvoyer. De plus, dans le cas de plusieurs objets dans le bloc try, cela semble confus et même si vous avez déclaré try intérieur, vous ne pouvez pas utiliser le bloc try extérieur.

3
Shivang Agarwal

Les réponses ci-dessus sont excellentes, mais il existe des cas où essayer avec des ressources n'aide pas.

Jetez un oeil à cet exemple de code:

private static byte[] getFileBytes(Collection<String> fileContent) throws CustomServiceException {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(baos))) {
            for (String fileLine : fileContent) {
                writer.append(fileLine);
                writer.newLine();
            }
        }
        return baos.toByteArray();
    } catch (IOException e) {
        throw new CustomServiceException(SC_INTERNAL_SERVER_ERROR, "Unable to serialize file data.");
    }
}

Dans cet exemple, vous ne pouvez pas simplement utiliser le bloc try-with-resources, car writer doit vider le tampon de sortie dans le flux de caractères sous-jacent; placer donc writer dans le bloc try-with-resources ne fera pas l'affaire et la méthode retournera un tableau vide. .

1
Dzmitry Hubin