在 Java 中关闭嵌套流和编写器的正确方法 [英] Correct way to close nested streams and writers in Java

查看:25
本文介绍了在 Java 中关闭嵌套流和编写器的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:这个问题及其大部分答案可以追溯到 Java 7 发布之前.Java 7 提供了 自动资源管理 功能可轻松完成此操作.如果您使用的是 Java 7 或更高版本,您应该提前到 Ross Johnson 的答案.

Note: This question and most of its answers date to before the release of Java 7. Java 7 provides Automatic Resource Management functionality for doing this easilly. If you are using Java 7 or later you should advance to the answer of Ross Johnson.

在 Java 中关闭嵌套流的最佳、最全面的方法是什么?例如,考虑设置:

What is considered the best, most comprehensive way to close nested streams in Java? For example, consider the setup:

FileOutputStream fos = new FileOutputStream(...)
BufferedOS bos = new BufferedOS(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);

我了解关闭操作需要投保(可能通过使用 finally 子句).我想知道的是,是否有必要明确确保嵌套流已关闭,还是仅确保关闭外部流 (oos) 就足够了?

I understand the close operation needs to be insured (probably by using a finally clause). What I wonder about is, is it necessary to explicitly make sure the nested streams are closed, or is it enough to just make sure to close the outer stream (oos)?

我注意到的一件事,至少在处理这个特定示例时,是内部流似乎只抛出 FileNotFoundExceptions.这似乎意味着从技术上讲,如果它们失败,则无需担心关闭它们.

One thing I notice, at least dealing with this specific example, is that the inner streams only seem to throw FileNotFoundExceptions. Which would seem to imply that there's not technically a need to worry about closing them if they fail.

这是一位同事写道:

从技术上讲,如果执行得当,关闭最外层流(oos)应该足够了.但实施似乎有缺陷.

Technically, if it were implemented right, closing the outermost stream (oos) should be enough. But the implementation seems flawed.

示例:BufferedOutputStream继承了FilterOutputStream的close(),它定义为:

Example: BufferedOutputStream inherits close() from FilterOutputStream, which defines it as:

 155       public void close() throws IOException {
 156           try {
 157             flush();
 158           } catch (IOException ignored) {
 159           }
 160           out.close();
 161       }

但是,如果flush()由于某种原因抛出运行时异常,那么out.close() 永远不会被调用.所以看起来最安全"(但丑陋)主要担心关闭 FOS,这会使文件保持打开状态.

However, if flush() throws a runtime exception for some reason, then out.close() will never be called. So it seems "safest" (but ugly) to mostly worry about closing FOS, which is keeping the file open.

当您绝对需要确保关闭嵌套流时,什么被认为是最好的方法?

What is considered to be the hands-down best, when-you-absolutely-need-to-be-sure, approach to closing nested streams?

是否有任何官方 Java/Sun 文档对此进行了详细处理?

And are there any official Java/Sun docs that deal with this in fine detail?

推荐答案

我通常会做以下事情.首先,定义一个基于模板方法的类来处理 try/catch 混乱

I usually do the following. First, define a template-method based class to deal with the try/catch mess

import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public abstract class AutoFileCloser {
    // the core action code that the implementer wants to run
    protected abstract void doWork() throws Throwable;

    // track a list of closeable thingies to close when finished
    private List<Closeable> closeables_ = new LinkedList<Closeable>();

    // give the implementer a way to track things to close
    // assumes this is called in order for nested closeables,
    // inner-most to outer-most
    protected final <T extends Closeable> T autoClose(T closeable) {
            closeables_.add(0, closeable);
            return closeable;
    }

    public AutoFileCloser() {
        // a variable to track a "meaningful" exception, in case
        // a close() throws an exception
        Throwable pending = null;

        try {
            doWork(); // do the real work

        } catch (Throwable throwable) {
            pending = throwable;

        } finally {
            // close the watched streams
            for (Closeable closeable : closeables_) {
                if (closeable != null) {
                    try {
                        closeable.close();
                    } catch (Throwable throwable) {
                        if (pending == null) {
                            pending = throwable;
                        }
                    }
                }
            }

            // if we had a pending exception, rethrow it
            // this is necessary b/c the close can throw an
            // exception, which would remove the pending
            // status of any exception thrown in the try block
            if (pending != null) {
                if (pending instanceof RuntimeException) {
                    throw (RuntimeException) pending;
                } else {
                    throw new RuntimeException(pending);
                }
            }
        }
    }
}

注意待处理"异常——这会处理关闭期间抛出的异常会掩盖我们可能真正关心的异常的情况.

Note the "pending" exception -- this takes care of the case where an exception thrown during close would mask an exception we might really care about.

finally 尝试首先从任何装饰流的外部关闭,因此如果您有一个包装 FileWriter 的 BufferedWriter,我们首先尝试关闭 BuffereredWriter,如果失败,仍然尝试关闭 FileWriter 本身.(注意 Closeable 的定义调用 close() 来忽略该调用,如果流已经关闭)

The finally tries to close from the outside of any decorated stream first, so if you had a BufferedWriter wrapping a FileWriter, we try to close the BuffereredWriter first, and if that fails, still try to close the FileWriter itself. (Note that the definition of Closeable calls for close() to ignore the call if the stream is already closed)

您可以按如下方式使用上述类:

You can use the above class as follows:

try {
    // ...

    new AutoFileCloser() {
        @Override protected void doWork() throws Throwable {
            // declare variables for the readers and "watch" them
            FileReader fileReader = 
                    autoClose(fileReader = new FileReader("somefile"));
            BufferedReader bufferedReader = 
                    autoClose(bufferedReader = new BufferedReader(fileReader));

            // ... do something with bufferedReader

            // if you need more than one reader or writer
            FileWriter fileWriter = 
                    autoClose(fileWriter = new FileWriter("someOtherFile"));
            BufferedWriter bufferedWriter = 
                    autoClose(bufferedWriter = new BufferedWriter(fileWriter));

            // ... do something with bufferedWriter
        }
    };

    // .. other logic, maybe more AutoFileClosers

} catch (RuntimeException e) {
    // report or log the exception
}

使用这种方法,您永远不必担心 try/catch/finally 再次处理关闭文件的问题.

Using this approach you never have to worry about the try/catch/finally to deal with closing files again.

如果这对您来说太重了,至少考虑遵循 try/catch 和它使用的pending"变量方法.

If this is too heavy for your use, at least think about following the try/catch and the "pending" variable approach it uses.

这篇关于在 Java 中关闭嵌套流和编写器的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆