Close multiple resources with AutoCloseable (try-with-resources)
Asked Answered
F

4

82

I know that the resource you pass with a try, will be closed automatically if the resource has AutoCloseable implemented. So far so good. But what do I do when i have several resources that I want automatically closed. Example with sockets;

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

So I know the socket will be closed properly, because it's passed as a parameter in the try, but how should the input and output be closed properly?

Frantz answered 31/5, 2015 at 2:7 Comment(0)
R
98

Try with resources can be used with multiple resources by declaring them all in the parenthesis. See the documentation

Relevant code excerpt from the linked documentation:

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());
        }
    }
}

If your objects don't implement AutoClosable (DataInputStream does), or must be declared before the try-with-resources, then the appropriate place to close them is in a finally block, also mentioned in the linked documentation.

Ridglea answered 31/5, 2015 at 2:10 Comment(0)
N
32

Don't worry, things will "just work". From Socket's documentation:

Closing this socket will also close the socket's InputStream and OutputStream.

I understand your concern about not explicitly calling close() on the input and output objects and in fact it's generally better to ensure all resources are automatically managed by the try-with-resources block, like this:

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

This would have the effect that the socket object would be "closed multiple times", but that should not do any harm (this is one of the reasons why it's generally advised that all implementations of close() be made idempotent).

Natashianatassia answered 4/12, 2015 at 12:54 Comment(1)
old post, but: only if no buffering is involved. If there's a BufferedOutputStream in there, and it has not been flushed, the data will be lost because the socket's output stream will be closed without that data being written out first.Braddy
H
10

In addition to the above answers, This is the improvement added in Java 9.

Java 9 try-with-resources makes an improved way of writing code. Now you can declare the variable outside the try block and use them inside try block directly.because of this you will get following benefits.

  • The Resources which it declared outside try( which is effectively final or final) can be automatically close by automatic resource management by just adding them in the try block.
    • You no need to re-refer objects declared outside try block nor need to close them manually as we need to do in Java 7.
    • It also helps to write clean code.

try-with-resource can we write like this in 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());
}

}

Here automatic resource management will automatically close both the objects dbCon & rs.

For the better understanding of the list of above define use cases please find some Java 7 code.

Example 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();
}

}

Example 2:

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

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

In the above samples you can see if the object is ouside try then either we need to close manually or re-refer it. Also in the case of multiple objects in the try block, it looks messy and even if you declared inside try then you can't use outside try block.

Harley answered 24/7, 2018 at 12:0 Comment(1)
In the first code snippet, does the try-with-resources close all 3, the Connection, the Statement, and the ResultSet? I'm fairly positive it will close just the Connection and the ResultSet, and rely on the closing of the Connection to close the Statement.Azeria
H
2

Answers above are great but there are some cases when try-with-resources doesn't help.

Take a look at this code example:

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.");
    }
}

In this example u can't just use try-with-resources block cause writer has to flush the output buffer to the underlying character stream so placing writer into try-with-resources block won't do the trick and method will return empty array.

Hawse answered 16/5, 2018 at 15:2 Comment(2)
ByteArrayOutputStream.close() is a no-op, and BufferedWriter.close() flushes the BufferedWriter. So you don't need to declare baos in a try-with-resources, though I don't see how it hurts to do so.) The following should work (exception handling omitted for brevity) : ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(baos))) { for (String fileLine : fileContent) { writer.append(fileLine); writer.newLine(); } } return baos.toByteArray();Hodman
You can just flush manually before doing the return. Thus said, this is not an answer to the question, but rather a comment...Sawyere

© 2022 - 2024 — McMap. All rights reserved.