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

查看:678
本文介绍了在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.

以下是同事所写的内容:

Here's what a colleague wrote:

从技术上讲,如果实施得当,关闭最外面的
流(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.

最后尝试从任何装饰流的外部关闭,所以如果你有一个包装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和它使用的待定变量方法。

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天全站免登陆