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

时间:2014-03-24 12:44:42

标签: java exception-handling try-catch-finally

我正在学习在线java课程Introduction to programming Using Java

在I / O章节中,以下代码引入了以下代码:

  

顺便说一句,在本程序结束时,您将在try语句中找到finally子句的第一个有用示例。当计算机执行try语句时,无论如何都保证执行finally子句中的命令。

该程序位于11.2.1部分的末尾,是一个简单的程序,只读取文件中的某些数字并以相反的顺序写入。

main方法中的相关代码是(数据是Reader,结果是Writer):

try {
    // Read numbers from the input file, adding them to the ArrayList.        
    while ( data.eof() == false ) {  // Read until end-of-file.
        double inputNumber = data.getlnDouble();
        numbers.add( inputNumber );
    }

    // Output the numbers in reverse order.        
    for (int i = numbers.size()-1; i >= 0; i--)
        result.println(numbers.get(i));

    System.out.println("Done!");        
} catch (IOException e) {
    // Some problem reading the data from the input file.
    System.out.println("Input Error: " + e.getMessage());
} finally {
    // Finish by closing the files, whatever else may have happened.
    data.close();
    result.close();
}

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

我想也许是因为理论上可能会有一些其他的RuntimeException会导致程序崩溃然后离开Reader&作家未公开,但那么该计划是否已经崩溃了呢?

9 个答案:

答案 0 :(得分:23)

您的想法是正确的:即使出现意外异常,finally块也会关闭资源。

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

请注意,仍有可能隐藏错误:如果data.close()抛出异常,result.close()永远不会被调用。

根据您的环境,有多种方法可以解决这个问题。

    在java 7中
  • 如果你可以使用try-with-resources

  • 如果您使用的是Spring,可能会有一个类似于JdbcTemplate

    的正确模板
  • 如果没有这些适用,是的,你必须在finally中进行一次尝试/终于。退出丑陋。你绝对应该至少将其提取到评论中建议的方法中。

  • 在java pre 8中概念上更清晰但更罗嗦是实现loan pattern。如果你不碰巧与scala / clojure / haskell开发人员合作,那么它可能会比其他任何事情更令人困惑。

答案 1 :(得分:8)

这是一个非常简单的原因:它是最安全的方式,在Java 7和try-with-resources之前,保证即使发现异常也会关闭资源。

如果你做了改变,请考虑一下发生了什么:

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之前关闭。


作为旁注,使用Java 6,关闭资源最安全的方法是使用Guava的Closer

答案 2 :(得分:6)

如果程序在此之后终止,则是,这也将关闭I / O资源。

但许多程序不会终止。有一些必须每周7天,每天24小时运行。因此,正确清理资源是必须的。

不幸的是,Java< 7仅提供内存的自动清理机制(垃圾收集)。使用Java 7,您将获得尝试插入漏洞的新“try-with-resource statement”。

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

尽管如此,上面的代码仍然存在问题:close()本身可以抛出异常,因此一些I / O资源可能仍然存在。您应该使用IOUtils.closeQuietly()之类的工具代替:

Reader reader = null;
try {
    reader = ...open...

    ...use reader...
} finally {
    IOUtils.closeQuietly(reader);
}

答案 3 :(得分:4)

来自Java doc:

  

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

关于你的担忧:

  

事实上程序已经崩溃了。无论如何。

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

答案 4 :(得分:2)

真。如果RuntimeException发生,最终将被执行从而关闭资源,这在理论上是不正确的。这是一个实际的场景。

此外,即使发生IOException(或许多其他人)。 finally子句阻止您编写相同的代码以进行多次关闭。

答案 5 :(得分:2)

这实际上是你形成的正确概念。当CheckedException出现IOException时,必须关闭所有资源。这是因为:

  1. 执行程序时,资源正在运行 访问。假设你已经改变了一些事情而你却没有改变 保存它们,你不会得到更新的文件。 (这发生在 你使用缓冲区 - 它们不会即时写入数据 写入)。

  2. 由于文件在JVM中打开,因此它有可能保持打开状态,您需要关闭应用程序 使用。因此,您需要close()资源,以便 缓冲区刷新,即保存更改。

  3. 例如:

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

    编辑:随着JDK1.7的出现,现在您可以使用try with resources,如下所示:

    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 实现了 AutoCloseable,因此不需要BufferedReader阻止。顾名思义,当剩下try (..)块时,它会自动关闭缓冲区。

答案 6 :(得分:2)

最后确保始终调用一段代码。因此,它是关闭连接的最佳场所。我建议将你的密切语句放在try catch中,因为finally块中的某些内容可能仍然出错:

finally {
    if (data != null) try { data.close() } catch(exception ex) { ex.printstacktrace() }
}

答案 7 :(得分:1)

如果抛出或不抛出异常,finally子句将确保关闭dataresult个流。否则,他们可能不会被关闭。

答案 8 :(得分:1)

A&#34;关闭&#34;方法不仅可以简单地通知操作系统不再需要资源。除此之外,封装各种形式的数据流的对象可以自己缓冲一定量的数据,并在它们被关闭时或者当累积了一定量的数据时将其传递给操作系统。将封装日志数据流的对象以大块的形式传递给OS可能比将日志事件单独传递给OS更有效,但如果程序在没有&#34;关闭的情况下死亡则更有效。日志文件,可能与诊断问题的任何人非常相关的信息将丢失。