在Java中打开流的正确方式是什么?

4

我将为物理学生进行一场有关Java的讲座,我想知道如何正确地打开一个文件。

在我的许多专业应用程序中,我做了类似以下的事情:

  BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("file")));
  try{
    ....
  }finally {
     bufferedWriter.close();
  }

在我看来,这是可以的,也就是说,读取器总是会被关闭。

当我为学生们制作这个示例时,我想知道如果InputStreamReader的构造函数抛出异常会发生什么 --- FileInputStream将保持打开状态,但是它不会被我的代码关闭(因为这些对象是在try-finally块之外创建的)。

所以这是正确的惯用法吗?如果是,那么为什么?如果打开流不是正确的惯用法,请指出正确的方式!

编辑:我正在寻找既正确又非常容易编写和理解的习惯用法,物理学生是编程初学者。

编辑:真傻我复制了错误的示例 --- 如果使用写入器而不是阅读器,则会变得更加复杂。

2个回答

5

使用输入流读取内容

在 Java 7 之前,您需要按照以下方式进行操作:

InputStream in = null;
try {
     in = new FileInputStream("simple.csv");
     BufferedReader buf = new BufferedReader(new InputStreamReader(in));
} finally {
  if (in != null) {
     try {
         in.close();
     } catch (IOException e) {}
  }
}

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

try (BufferedReader buf = new BufferedReader(...)) {}

编辑:为什么我之前没有关闭buf?请查看BufferedReader.close()的源代码。

public void close() throws IOException {
    synchronized (lock) {
        if (in == null)
            return;
        in.close();
        in = null;
        cb = null;
    }
}

使用输出流进行写作

编辑2: 相同的原则适用于writer。然而,如果您真的很想在发生IOException时刷新流,则必须检查writerstream是否为null并分别尝试关闭它们。这会增加很多额外的代码。可能会像这样:

BufferedWriter buf = null;
OutputStream out = null;
try {
    out = new FileOutputStream("file");
    buf = new BufferedWriter(new OutputStreamWriter(out));
} finally {
  if (buf != null) {
     try { buf.close(); } catch (IOException ex){}
  }
  if (out != null) {
     try { out.close(); } catch (IOException ex){}
  }
}

这不太美观。您可以引入一个帮助程序来关闭流,或者研究Java 7或Apache IOUtils


不太对......(编辑:没关系,你已经解决了我的两个异议。我会使用 BufferedReader buf = null 只是为了更明确) - Jason S
如果 InputStreamReader 抛出异常,这不会关闭流。此外,因为 buf 可能为 null,所以最终会导致另一个 NullPointerException - Neet
@jb。正确。除非您想重复使用缓冲读取器,否则没有必要这样做。当然,您可以像测试和关闭“in”一样进行操作。 - Johan Sjöberg
据我所知,在关闭缓冲区之前会有 ---,因为缓冲区需要刷新。 - jb.
是的,没错。但是,如果发生 IOException,你真的想要刷新吗? - Johan Sjöberg
显示剩余2条评论

0
在这种特殊情况下(嵌套构造函数),FileInputStream将保持打开状态。如果您确实需要确保流被关闭,您必须像这样操作:
final FileInputStream fis = new FileInputStream("");
try
{
    final BufferedReader br = new BufferedReader(new InputStreamReader(fis));
    // ...
}
finally
{
    fis.close();
}

关闭 FileInputStream 应该就足够了。

编辑:同样的逻辑也可应用于写入器。如果打开 FileOutputStream 失败,它会抛出异常,防止 try/finally 块执行。如果任何其他写入器构造函数失败,输出流仍将通过 final 子句关闭。


实际上,我只需要找到正确的代码示例,而我不确定在FileInputStream构造函数中添加额外的代码行以检查错误是否值得努力。 - jb.
另一方面,我们唯一预期会出现异常的地方是FileInputStream的构造函数,因为它要打开的文件可能不存在。 - Neet

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