使用自动关闭(try-with-resources)关闭多个资源

我知道如果资源已经实现了 AutoCloseable,那么您通过一次尝试传递的资源将自动关闭。目前为止还不错。但是当我有几个想要自动关闭的资源时,我该怎么做呢。插座实例;

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

因此,我知道套接字将被正确关闭,因为它在 try 中作为参数传递,但是如何正确关闭输入和输出呢?

58889 次浏览

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.

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

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.

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.