【问题标题】:Does close ever throw an IOException?close 是否会抛出 IOException?
【发布时间】:2010-10-09 23:13:12
【问题描述】:

在此处提供了一些答案并阅读了一些 cmets 之后,似乎在实践中,文件 I/O 永远不会在关闭时抛出 IOException。

是否存在在 Stream/Reader/Writer 上调用 close 实际上会引发 IOException 的任何情况?

如果真的抛出了异常,应该如何处理?

【问题讨论】:

  • 我还注意到 PrintWriter 甚至没有 throws 子句....

标签: java ioexception


【解决方案1】:

我发现了两种情况:

  • 当缓冲区中仍有数据需要刷新时断开网络连接。
  • 当缓冲区中仍有数据需要刷新时,让文件系统填满(或达到用户对文件大小的限制)。

这两个示例都依赖于缓冲区中仍有数据时发生的事情。 Close 在文件关闭之前刷新缓冲区,因此如果将数据写入文件时出错,它会抛出 IOException。

如果执行以下代码,将要在网络驱动器上创建的文件的名称传递给它,然后在按下回车键之前拔下网络电缆,这将导致程序在关闭时抛出 IOException。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class Test
{
    public static void main(final String[] argv)
    {
        final File file;

        file = new File(argv[0]);
        process(file);
    }

    private static void process(final File file)
    {
        Writer writer;

        writer = null;

        try
        {
            writer = new FileWriter(file);
            writer.write('a');
        }
        catch(final IOException ex)
        {
            System.err.println("error opening file: " + file.getAbsolutePath());
        }
        finally
        {
            if(writer != null)
            {
                try
                {
                    try
                    {
                        System.out.println("Please press enter");
                        System.in.read();
                    }
                    catch(IOException ex)
                    {
                        System.err.println("error reading from the keyboard");
                    }

                    writer.close();
                }
                catch(final IOException ex)
                {
                    System.err.println("See it can be thrown!");
                }
            }
        }
    }
}

从 Java 7 开始,您可以使用 try-with-resources 来摆脱这种混乱(删除了 close() 操作的显式异常生成代码):

private static void process(final File file) {
    try (final Writer writer = new FileWriter(file)) {
        writer.write('a');
    } catch (final IOException e) {
        // handle exception
    }
}

这将自动神奇地处理close() 中的异常,并在内部执行明确的null 检查。

【讨论】:

【解决方案2】:

当它确实发生时,应该像处理任何其他 IOException 一样处理它,而不是像你经常看到的推荐那样默默地忽略它。我猜,假设是,既然你已经使用了流,那么它是否被正确清理并不重要。

但是,正确清理很重要。如果close() 操作确实引发了异常,它很可能涉及刷新一些输出、提交一些事务(在您认为是只读的数据库连接的情况下)等等——绝对不是应该忽略的事情。而且,由于这种情况很少见,因此您不会通过中止操作来显着损害应用程序的可靠性。

【讨论】:

  • 是的,但是如果您只是打开文件进行阅读呢?那么刷新或提交的问题就不适用了。
  • 某些形式的“提交”可能适用,例如释放锁。我同意在关闭任何类型的“只读”资源时看到异常引发是非常令人惊讶的......这将是一个非常罕见的事件,我肯定想记录它。用空的 catch 块忽略它不会提高程序的质量。
【解决方案3】:

对于文件,您可能不会在 close() 上看到经常抛出 IOException,但您肯定会在非文件 I/O 中看到它,例如关闭到网络的套接字。

Here's an example 的 Java 错误,其中关闭 UDP 套接字最终导致抛出 IOException。

【讨论】:

  • 是的,任何可以“消失”的东西都可能导致这种情况发生。硬盘崩溃也可能发生。
【解决方案4】:

特别是FileInputStream.close,即使您的硬盘着火,它也不会抛出异常。大概套接字输入是相同的。对于输出流,您也可能正在刷新。直到最近[查看时间戳]BufferedOutputStream 过去如果flush 抛出,则无法关闭底层流。

(@MaartenBodewes 希望我指出,API 文档没有指定 FileInputStream.close not throwing。在发帖时,习惯上省略提到这与 Sun JDK 相关的条款(现在已知如 Oracle JDK 和 OpenJDK)。看来,Android 过去使用的一个名为 Apache Harmony 的不知名的重新实现可能具有不同的行为。可能其他实现或 OpenJDK 版本也可能抛出。)

【讨论】:

  • +1 表示“即使你的硬盘着火了”。我想在 Java 文档中看到“如果你的硬盘着火了,就会抛出 IOException”;)
  • 然而,已发布的 API 没有做出这样的承诺:download.oracle.com/javase/6/docs/api/java/io/…
  • @Raedwald 确实如此。问题是关于“在实践中”。
  • @TomHawtin-tackline this one 怎么样?在保持流打开的同时删除文件句柄似乎就足够了......我什至不会称之为异常。
  • @MaartenBodewes 可能应该提到这不是 API 保证,并且实现(在这种情况下为 Android 3.0)可能会有所不同。
【解决方案5】:

检查调用 close 时会发生什么,异常隐藏如何影响您以及您可以采取哪些措施:blog post

【讨论】:

    猜你喜欢
    • 2014-11-15
    • 2021-03-05
    • 2012-01-13
    • 2012-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-14
    相关资源
    最近更新 更多