Java 这个finally子句包含close()调用的原因是什么

Java 这个finally子句包含close()调用的原因是什么,java,exception-handling,try-catch-finally,Java,Exception Handling,Try Catch Finally,我正在学习在线java课程 在关于I/O的章节中,介绍了以下代码和以下声明: 顺便说一下,在本程序的末尾,您将在try语句中找到finally子句的第一个有用示例。当计算机执行try语句时,无论发生什么情况,其finally子句中的命令都保证被执行 该程序位于第节的末尾,是一个简单的程序,只需从文件中读取一些数字,然后按相反顺序写入 主方法中的相关代码是(数据是读卡器,结果是写卡器): 所以我想知道为什么finally子句在这种情况下很有用,因为try或catch子句没有其他退出点。关闭方法不能

我正在学习在线java课程

在关于I/O的章节中,介绍了以下代码和以下声明:

顺便说一下,在本程序的末尾,您将在try语句中找到finally子句的第一个有用示例。当计算机执行try语句时,无论发生什么情况,其finally子句中的命令都保证被执行

该程序位于第节的末尾,是一个简单的程序,只需从文件中读取一些数字,然后按相反顺序写入

主方法中的相关代码是(数据是读卡器,结果是写卡器):

所以我想知道为什么finally子句在这种情况下很有用,因为try或catch子句没有其他退出点。关闭方法不能只在主体中吗


我想这可能是因为理论上可能存在其他运行时异常,这些异常会使程序崩溃,然后使读写器无法关闭,但程序崩溃的事实不会让它们关闭吗?

在抛出或未抛出异常的情况下,
finally
子句将确保
数据流
结果流
均已关闭。否则,它们可能无法关闭。

来自Java文档:

当try块退出时,finally块始终执行。这确保即使发生意外异常,也会执行finally块。但最后,它不仅仅适用于异常处理——它允许程序员避免因返回、继续或中断而意外绕过清理代码。将清理代码放在finally块中始终是一种好的做法,即使在没有预期异常的情况下也是如此

以及您对以下方面的关注:

程序崩溃这一事实无论如何都让他们无法接受


资源是在
OS
级别分配的,而不是在您的程序中分配的,因此如果您的程序没有机会清理,那么资源将在没有真正使用的情况下分配。

这是一个非常简单的原因:这是最安全的方法,在Java 7之前尝试使用资源,确保即使捕获到异常,您的资源也已关闭

考虑一下如果你这样做了会发生什么:

try {
    // some code, then

    resource.close();
} catch (SomeException e) {
    // etc
}
如果在关闭资源之前抛出了
SomeException
,则可能会泄漏资源。另一方面,将
resource.close()
放入
finally
,可以保证无论发生什么情况,它都会被关闭

对于Java 7,您可以使用以下内容:

try (
    final InputStream in = Files.newInputStream(Paths.get("somefile"));
    // Others
) {
    // work with "in" and others
} catch (Whatever e) {
}
然后,您的资源将在
catch
之前关闭



另外,使用Java6关闭资源最安全的方法是使用番石榴。如果出现
RuntimeException
,finally将被执行,从而关闭资源,这在理论上是不正确的。这是一个实际情况


此外,即使发生了
IOException
(或您捕获的许多其他异常)
finally
子句防止您多次编写相同的代码来关闭。您的想法是正确的:finally块将关闭资源,即使发生意外异常

如果这样的异常使整个应用程序崩溃,那么这一点也不重要,这一点您是对的,但是通过查看此代码,您无法确定是否存在这种情况。可能还有其他异常处理程序捕获该异常,因此将结束逻辑放在finally块中是一种良好且正确的做法

请注意,仍然可能有一个bug隐藏:如果
data.close()
抛出异常,
result.close()
将永远不会被调用

根据您的环境,对于如何修复bug有不同的风格

  • 在Java7FF中,您可以使用

  • 如果您使用的是Spring,那么可能会有一个与

  • 如果这些都不适用,是的,您必须在finally中尝试/finally。别闹了。您绝对应该至少将其提取到注释中建议的方法中

  • 概念上更清晰但在JavaPre8中更冗长的是实现。如果您没有与scala/clojure/haskell开发人员一起工作,那么可能会比其他任何东西更令人困惑


这实际上是你形成的正确概念。当发生
CheckedException
类似
IOException
的异常时,必须关闭所有资源。这是因为:

  • 当您执行程序时,资源正在 访问。假设你改变了一些事情,但你没有 保存后,您将无法获得更新的文件。(这种情况发生在 您使用缓冲区-它们不会立即写入数据,而是 分块写)

  • 由于该文件是在JVM中打开的,因此它很可能会保持打开状态,您需要关闭正在运行的应用程序 使用。因此,您需要
    close()
    资源,以便 刷新缓冲区,即保存更改

  • 例如:

    try {
        BufferedReader br = new BufferredReader (new FileReader("Example.txt"));
    
        ArrayList<String> lines = new ArrayList<>();
        String line;
        while ( (line = br.readLine()) != null ) {
            lines.add(line);
        }
    catch (IOException ie) {
        //Error handling.
    } finally {
        br.close();
    }
    

    现在,不需要
    finally
    块,因为
    BufferedReader
    实现了。顾名思义,当
    try(…)
    块离开时,它会自动关闭缓冲区。

    如果程序在此之后终止,那么是的,这也会关闭I/O资源

    但许多程序不会终止。有些设备必须24/7运行多年。因此,正确清理您的资源是必不可少的

    不幸的是,Java<7只提供了一种自动的内存清理机制(垃圾收集)。使用Java7,您将得到一个新的“”来尝试填补漏洞

    除非您可以使用此版本,否则您必须自己进行清理

    也就是说,上面的代码仍然有缺陷:
    close()
    本身会引发异常,因此一些I/O资源可能仍然存在。您应该使用以下工具:

    Fi
    try {
        BufferedReader br = new BufferredReader (new FileReader("Example.txt"));
    
        ArrayList<String> lines = new ArrayList<>();
        String line;
        while ( (line = br.readLine()) != null ) {
            lines.add(line);
        }
    catch (IOException ie) {
        //Error handling.
    } finally {
        br.close();
    }
    
    try (BufferedReader br = new BufferredReader (new FileReader("Example.txt"));) {
        ArrayList<String> lines = new ArrayList<>();
        String line;
        while ( (line = br.readLine()) != null ) {
            lines.add(line);
        }
    catch (IOException ie) {
        //Error handling.
    }
    
    Reader reader = null;
    try {
        reader = ...open...
    
        ...use reader...
    } finally {
        IOUtils.closeQuietly(reader);
    }