【问题标题】:Closing streams/sockets and try-catch-finally关闭流/套接字和 try-catch-finally
【发布时间】:2011-08-27 07:42:49
【问题描述】:

这是一个我有疑问的例子(它来自另一个 SO 问题):

public static void writeToFile (final String filename)
{
    PrintWriter out = null;     
    FileOutputStream fos = null;

    try
    {
        fos = new FileOutputStream(filename);
        out = new PrintWriter(new BufferedWriter(
                              new OutputStreamWriter(fos, "UTF-8")));

        for (final String line : log)
        {
            out.println(line);
        }

        out.flush();
        out.close();
    }
    catch (final Exception e)
    {
        System.err.println("Unable to write log to file.");
    }
    finally
    {
        if (fos != null)
        {
            try
            {
                fos.close();
            }
            catch (final IOException e)
            {
                System.err.println("Unable to write log to file.");
            }
        }
    }
}

现在我认为这段代码可以正常工作,并在应该的地方释放所有资源等。我的问题是:

  1. 我为什么要关闭try-catch-finallyfinally 部分中的FileOutputStream?当然我可以将代码按顺序放在try-catch 之后?

  2. 为什么我必须单独关闭FileOutputStream 而不是简单地将new OutputStreamWriter(fos, ... 替换为new OutputStreamWriter(new FileOutputStream(filename), ...?如果我先关闭 FileOutputStream,那会自动关闭其余部分并释放资源吗?同样的问题也适用于套接字,如果我关闭套接字连接,是否会自动关闭流读取器/写入器并释放资源?

  3. 我被反复告知要确保我使用“UTF-8”读写流,因为不同的系统具有不同的字符集(或类似的字符集)。这在读取/写入 RAW 字节数据(例如来自非文本文件或加密结果)时是否仍然适用,因为我认为字符集只能处理文本字符?

【问题讨论】:

  • 移动代码'out.close();' for out = new PrintWriter(...) 最终阻塞,与 'fos = new FileOutputStream(filename);' 的定义相同
  • 您写道:“我现在找不到线程”。也许您的意思是 22answers.com 上的 "When to use which Writer subclass in Java; common practices" 线程。
  • @mKorbel - 谢谢。 @Marnix Klooster - 是的,它实际上是SO帖子的副本。 @Jonathon - 感谢您的编辑。

标签: java sockets character-encoding io try-catch


【解决方案1】:

关于第一点 - 如果您的代码抛出错误,而不是异常,如果 close 语句不在 finally 中,则 FileOutputStream 不会被关闭。在您捕获异常时,这在您的情况下有点人为,但是在更典型的情况下,将捕获更具体的异常,除非您整理 finally 块中的资源,否则该方法的执行可能会在它到达之前终止将关闭资源的代码。

【讨论】:

    【解决方案2】:

    为什么要在 try-catch-finally 的 finally 部分关闭 FileOutputStream?

    您可以将close() 放在所有写入/读取操作块的末尾,但如果其中出现问题(读取/写入),您将到达异常处理块并且不会关闭任何流.如果您选择将close() 放在异常处理块中并且一切正常,您猜怎么着?...不会关闭任何流。因此,您可以在两个代码块中执行此操作,但这样代码的可读性会降低。所以把它放在 finally 块上可以确保无论哪种方式它都会被关闭。

    其次,您应该只关闭已链接的最后一个流。所以如果你有这个。

    fos = new FileOutputStream(filename);
            out = new PrintWriter(new BufferedWriter(
                                  new OutputStreamWriter(fos, "UTF-8")));
    

    你只需要

    out.close();
    

    这将关闭与输出流关联的其他流。

    UTF-8 部分取决于您尝试读取的数据编码类型,如果您编码 UTF-8,则解码 UTF-8。

    【讨论】:

    • 您确定“这将关闭与输出流关联的其他流”。部分?这在 PrintWriter 类中的关闭的 javadoc 中没有记录。
    • 如果在构造装饰器时发生异常,那么[仅]关闭最后一个流会导致泄漏。
    【解决方案3】:
    1. 通常,在catch 块中,有人会抛出RuntimeException,这会导致该方法返回。如果你没有在 finally 中清理你的资源,它们将不会被清理。在您的示例中,您应该在 finally 块之后关闭,因为您没有强行退出。
    2. 在包装流上调用 close() 应该关闭子流。如果您编写自己的包装流,则其 close 方法应将 close() 委托给其子级。这就是为什么我说应该,由实现者来调用它的孩子的 close()。
    3. 如果您正在写入原始字节数据,则不需要指定 utf-8。它是一种字符编码,有助于显示各种语言的字符。

    【讨论】:

      【解决方案4】:
      1. 因为如果你的代码抛出一个未处理的异常,try-catch bloc 之后的 sn-p 将永远不会被执行。例如,NullPointerExcpetion 就是这种情况。

      2. 您不必这样做。如果你关闭一个流,它的任何封闭流也将被关闭。

      3. 没有。仅当将字节转换为字符(或相反)时才会出现这种情况。您将字符集指定为 OutputStreamWriter,它负责将字符转换为字节。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-22
        • 2014-11-27
        • 2014-09-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多