关于java:close是否会抛出IOException?

Does close ever throw an IOException?

在这里提供了一些答案,并阅读了一些评论之后,在实践中,IOException似乎永远不会因为文件I/O而被关闭。

在某些情况下,对流/读卡器/编写器调用Close实际上会引发IOException吗?

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


我发现了两个案例:

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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,您可以使用尝试资源来摆脱这种混乱(删除EDCOX1×0操作的显式异常生成代码):

1
2
3
4
5
6
7
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检查。


当它真的发生时,它应该像其他任何IOException一样被处理,而不是像你经常看到的那样被默默地忽略。我想,假设是,既然您已经使用完流,那么它是否被正确地清理就无关紧要了。

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


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

下面是一个Java错误的例子,其中关闭一个UDP套接字最终导致一个IOExt被抛出。


特别是FileInputStream.close,即使你的硬盘着火了,它也不会抛出。大概对于套接字输入是相同的。对于输出流,您也可能正在刷新。直到最近(见时间戳),BufferedOutputStream用于在flush抛出时无法关闭底层流。

(@maartenbodewes希望我指出,API文档没有指定EDOCX1[5]不抛出。在发帖时,人们习惯于省略提到这与Sun JDK(现在称为Oracle JDK和OpenJDK)相关的条款。看起来,一个鲜为人知的前重新实现ApacheHarmony,Android过去使用它,可能有不同的行为。其他可能的实现,或者OpenJDK的版本,也可能抛出。)


检查一下当调用close时会发生什么,异常隐藏会如何影响你,以及你能做些什么:博客文章。