close方法会抛出IOException吗?

29

在回答了一些问题并阅读了一些评论后,实际上在文件I/O的关闭操作中,IOException从未被抛出。

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

如果确实抛出异常,应该如何处理?


我还注意到PrintWriter甚至没有throws子句... - Andrei Vajna II
5个回答

29

我发现了两种情况:

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

这两种情况都取决于在缓冲区仍有数据的情况下发生某些事情。Close刷新缓冲区后再关闭文件,因此如果写入数据到文件时出现错误,它会抛出一个IOException。

如果您执行以下代码并传递要在网络驱动器上创建的文件的名称,然后在按下回车键之前拔掉网络电缆,它将导致程序在close中抛出一个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检查。


玩得开心,你即将获得那个徽章;-) - Joachim Sauer
哇,你知道吗,在文件输入流上生成另一个是可能的。http://stackoverflow.com/questions/5649500/ioexception-while-closing-bufferedinputstream - Maarten Bodewes

17
当发生这种情况时,应像处理任何其他 IOException 一样处理,而不是像经常推荐的那样被忽略。我猜测的假设是,既然你已经使用完流,那么它是否被正确清理就无关紧要了。
然而,正确清理很重要。如果 close() 操作引发异常,很可能涉及到刷新一些输出、提交某些事务(在你认为是只读的数据库连接的情况下),等等——绝对不应该被忽视。而且,由于这种情况很少发生,因此通过中止操作并没有显著影响应用程序的可靠性。

3
是的,但是如果你只是打开文件进行读取呢?那么刷新或提交的问题就不适用了。 - sleske
2
某些形式的“提交”可能适用,例如释放锁定。我同意,在关闭任何类型的“只读”资源时引发异常会非常令人惊讶...这将是如此罕见的事件,我肯定希望记录它。用空的catch块忽略它不会提高程序的质量。 - erickson

15
对于文件,你可能不经常在close()方法中看到IOException的抛出,但是对于非文件I/O,比如关闭与网络的套接字,你肯定会看到它的出现。
这里有一个Java bug的例子,关闭UDP套接字最终导致抛出IOException。

是的,任何可以“消失”的东西都可能导致这种情况发生。硬盘崩溃也可能会引起这种情况。 - TofuBeer

5

特别是FileInputStream.close,即使你的硬盘着火了也不会抛出异常。可能对于套接字输入也是一样的。对于输出流,您还可以刷新。直到最近[请参见时间戳]BufferedOutputStreamflush抛出异常时无法关闭底层流。

(@MaartenBodewes希望我指出API文档未规定FileInputStream.close不会抛出异常。在发布此帖子时,通常省略了提到这与Sun JDK(现在称为Oracle JDK和OpenJDK)有关的条款。似乎Android曾经使用的一个名为Apache Harmony的晦涩的旧重新实现可能具有不同的行为。其他实现或OpenJDK版本也可能抛出异常。)


+1 对于“即使您的硬盘着火也要执行”。我想在Java文档中看到,“如果您的硬盘着火,抛出IOException异常”;) - aioobe
@TomHawtin-tackline,关于这个问题,移除文件句柄而保持流开启似乎已经足够了...我甚至不认为这是一个特殊的异常。 - Maarten Bodewes
1
@MaartenBodewes 应该明确提到这不是API的保证,实现(在这种情况下是Android 3.0)可能会有所不同。 - Tom Hawtin - tackline
@MaartenBodewes 我相信您也具备编辑答案所需的声誉,更改将立即应用。 - Tom Hawtin - tackline
当然可以,但我从来不会根本性地改变答案。这就是我一开始发表评论的原因。 - Maarten Bodewes
显示剩余3条评论

3

本文讨论了调用 close 方法时可能发生的情况,异常隐藏如何影响您以及您可以采取什么措施解决这些问题:博客文章


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接