Close multiple resources with AutoCloseable (try-w

2019-02-01 16:42发布

问题:

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?

回答1:

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.



回答2:

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).



回答3:

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.



回答4:

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.