【问题标题】:Close multiple resources with AutoCloseable (try-with-resources)使用 AutoCloseable (try-with-resources) 关闭多个资源
【发布时间】:2015-08-13 17:03:37
【问题描述】:

我知道如果资源实现了 AutoCloseable,您尝试传递的资源将自动关闭。到目前为止,一切都很好。但是,当我有几个想要自动关闭的资源时,我该怎么办。套接字示例;

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

所以我知道socket会正确关闭,因为在try中是作为参数传递的,但是输入输出应该如何正确关闭呢?

【问题讨论】:

    标签: java sockets datainputstream try-with-resources autocloseable


    【解决方案1】:

    Try with resources 可以通过在括号中声明它们来与多个资源一起使用。见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());
            }
        }
    }
    

    如果您的对象没有实现 AutoClosableDataInputStream 实现),或者必须在 try-with-resources 之前声明,那么关闭它们的适当位置是在 finally 块中,也在链接的文档。

    【讨论】:

      【解决方案2】:

      别担心,一切都会“正常工作”。来自Socket's documentation

      关闭这个socket也会关闭这个socket的InputStream和OutputStream。

      我理解您对未在输入和输出对象上显式调用 close() 的担忧,事实上,通常最好确保所有资源都由 try-with-resources 块自动管理,如下所示:

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

      这会产生套接字对象将被“关闭多次”的效果,但这不应该造成任何伤害(这是通常建议将close() 的所有实现设为幂等的原因之一)。

      【讨论】:

        【解决方案3】:

        除了上面的答案,这是Java 9中添加的改进。

        Java 9 try-with-resources 改进了编写代码的方式。现在您可以在 try 块之外声明变量,并直接在 try 块中使用它们。因此您将获得以下好处。

        • 它在 try 之外声明的资源(实际上是 final 或 final)可以通过自动资源管理自动关闭,只需将它们添加到 try 块中。
          • 您无需重新引用在 try 块外声明的对象,也无需像在 Java 7 中那样手动关闭它们。
          • 它还有助于编写干净的代码。

        try-with-resource 我们可以在 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());
        }
        

        }

        这里自动资源管理会自动关闭dbCon和rs这两个对象。

        为了更好地理解上面定义的用例列表,请查找一些 Java 7 代码。

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

        }

        示例 2:

        // BufferedReader is declared outside try() block
            BufferedReader br = new BufferedReader(new FileReader("C://readfile/input.txt"));
        
            try (BufferedReader inBr = br) {
                    // ...
                }
            } catch (IOException e) {
                // ...
            }
        

        在上面的示例中,您可以查看对象是否在外面尝试然后我们需要手动关闭或重新引用它。同样在 try 块中有多个对象的情况下,它看起来很乱,即使您在 try 中声明了,也不能在 try 块之外使用。

        【讨论】:

        • 在第一个代码 sn-p 中,try-with-resources 是否关闭所有 3 个、Connection、Statement 和 ResultSet?我相当肯定它只会关闭 Connection 和 ResultSet,并依靠关闭 Connection 来关闭 Statement。
        【解决方案4】:

        上面的答案很好,但在某些情况下 try-with-resources 没有帮助。

        看看这个代码示例:

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

        在这个例子中,你不能只使用 try-with-resources 块,因为 writer 必须将输出缓冲区刷新到底层字符流,因此将 writer 放入 try-with-resources 块不会起到作用,方法会返回空数组。

        【讨论】:

        • ByteArrayOutputStream.close() 是无操作的,BufferedWriter.close() 会刷新 BufferedWriter。所以你不需要需要在try-with-resources中声明baos,虽然我不明白这样做有什么坏处。)以下应该有效(为简洁起见省略了异常处理) : ByteArrayOutputStream baos = new ByteArrayOutputStream();尝试 (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(baos))) { for (String fileLine : fileContent) { writer.append(fileLine); writer.newLine(); } } return baos.toByteArray();
        猜你喜欢
        • 1970-01-01
        • 2011-10-16
        • 2020-01-01
        • 2016-09-29
        • 2013-09-12
        • 2019-09-16
        • 2017-12-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多