java 是否有必要分别关闭每个嵌套的 OutputStream 和 Writer?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28276423/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it necessary to close each nested OutputStream and Writer separately?
提问by Adon Smith
I am writing a piece of code:
我正在写一段代码:
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
Do I need to close every stream or writer like the following?
我是否需要像下面这样关闭每个流或编写器?
gzipOutputStream.close();
bw.close();
outputStream.close();
Or will just closing the last stream be fine?
还是关闭最后一个流就可以了?
bw.close();
回答by T.J. Crowder
Assuming all the streams get created okay, yes, just closing bw
is fine with those stream implementations; but that's a big assumption.
假设所有的流获得创建好了,是的,刚刚闭幕bw
的精细与流实现; 但这是一个很大的假设。
I'd use try-with-resources(tutorial) so that any issues constructing the subsequent streams that throw exceptions don't leave the previous streams hanging, and so you don't have to rely on the stream implementation having the call to close the underlying stream:
我将使用try-with-resources(教程),以便构建引发异常的后续流的任何问题都不会使先前的流挂起,因此您不必依赖具有关闭调用的流实现底层流:
try (
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
) {
// ...
}
Note you no longer call close
at all.
请注意,您根本不再打电话close
了。
Important note: To have try-with-resources close them, you mustassign the streams to variables as you open them, you cannot use nesting. If you use nesting, an exception during construction of one of the later streams (say, GZIPOutputStream
) will leave any stream constructed by the nested calls inside it open. From JLS §14.20.3:
重要说明:要让 try-with-resources 关闭它们,您必须在打开它们时将流分配给变量,不能使用嵌套。如果您使用嵌套,则在构造较晚的流之一(例如,GZIPOutputStream
)期间的异常将使由其内部的嵌套调用构造的任何流保持打开状态。从JLS §14.20.3:
A try-with-resources statement is parameterized with variables(known as resources) that are initialized before execution of the
try
block and closed automatically, in the reverse order from which they were initialized, after execution of thetry
block.
try-with-resources 语句使用变量(称为资源)进行参数化,这些变量在
try
块执行之前初始化并在块执行之后按照与它们初始化的相反顺序自动关闭try
。
Note the word "variables" (my emphasis).
注意“变量”这个词(我的重点)。
E.g., don't do this:
例如,不要这样做:
// DON'T DO THIS
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new GZIPOutputStream(
new FileOutputStream(createdFile))))) {
// ...
}
...because an exception from the GZIPOutputStream(OutputStream)
constructor (which says it may throw IOException
, and writes a header to the underlying stream) would leave the FileOutputStream
open. Since some resources have constructors that may throw and others don't, it's a good habit to just list them separately.
...因为来自GZIPOutputStream(OutputStream)
构造函数的异常(它说它可能 throw IOException
,并将标头写入底层流)将保持FileOutputStream
打开状态。由于某些资源具有可能抛出的构造函数而其他资源没有,因此将它们分开列出是一个好习惯。
We can double-check our interpretation of that JLS section with this program:
我们可以用这个程序仔细检查我们对 JLS 部分的解释:
public class Example {
private static class InnerMost implements AutoCloseable {
public InnerMost() throws Exception {
System.out.println("Constructing " + this.getClass().getName());
}
@Override
public void close() throws Exception {
System.out.println(this.getClass().getName() + " closed");
}
}
private static class Middle implements AutoCloseable {
private AutoCloseable c;
public Middle(AutoCloseable c) {
System.out.println("Constructing " + this.getClass().getName());
this.c = c;
}
@Override
public void close() throws Exception {
System.out.println(this.getClass().getName() + " closed");
c.close();
}
}
private static class OuterMost implements AutoCloseable {
private AutoCloseable c;
public OuterMost(AutoCloseable c) throws Exception {
System.out.println("Constructing " + this.getClass().getName());
throw new Exception(this.getClass().getName() + " failed");
}
@Override
public void close() throws Exception {
System.out.println(this.getClass().getName() + " closed");
c.close();
}
}
public static final void main(String[] args) {
// DON'T DO THIS
try (OuterMost om = new OuterMost(
new Middle(
new InnerMost()
)
)
) {
System.out.println("In try block");
}
catch (Exception e) {
System.out.println("In catch block");
}
finally {
System.out.println("In finally block");
}
System.out.println("At end of main");
}
}
...which has the output:
...输出:
Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost In catch block In finally block At end of main
Note that there are no calls to close
there.
请注意,那里没有调用close
。
If we fix main
:
如果我们修复main
:
public static final void main(String[] args) {
try (
InnerMost im = new InnerMost();
Middle m = new Middle(im);
OuterMost om = new OuterMost(m)
) {
System.out.println("In try block");
}
catch (Exception e) {
System.out.println("In catch block");
}
finally {
System.out.println("In finally block");
}
System.out.println("At end of main");
}
then we get the appropriate close
calls:
然后我们得到适当的close
调用:
Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost Example$Middle closed Example$InnerMost closed Example$InnerMost closed In catch block In finally block At end of main
(Yes, two calls to InnerMost#close
is correct; one is from Middle
, the other from try-with-resources.)
(是的,两次调用InnerMost#close
是正确的;一个来自Middle
,另一个来自 try-with-resources。)
回答by Peter Lawrey
You can close the outer most stream, in fact you don't need to retain all the streams wrapped and you can use Java 7 try-with-resources.
您可以关闭最外面的流,实际上您不需要保留所有包裹的流,您可以使用 Java 7 try-with-resources。
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new GZIPOutputStream(new FileOutputStream(createdFile)))) {
// write to the buffered writer
}
If you subscribe to YAGNI, or you-aint-gonna-need-it, you should be only adding code you actually need. You shouldn't be adding code you imagine you might need but in reality doesn't do anything useful.
如果您订阅了 YAGNI,或者您不需要它,那么您应该只添加您实际需要的代码。您不应该添加您想象中可能需要的代码,但实际上并没有做任何有用的事情。
Take this example and imagine what could possibly go wrong if you didn't do this and what the impact would be?
以这个例子为例,想象一下如果你不这样做可能会出现什么问题以及会产生什么影响?
try (
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
) {
// ...
}
Lets start with FileOutputStream which calls open
to do all the real work.
让我们从 FileOutputStream 开始,它调用open
来完成所有实际工作。
/**
* Opens a file, with the specified name, for overwriting or appending.
* @param name name of file to be opened
* @param append whether the file is to be opened in append mode
*/
private native void open(String name, boolean append)
throws FileNotFoundException;
If the file is not found, there is no underlying resource to close, so closing it won't make any difference. If The file exists, it should be throwing a FileNotFoundException. So there is nothing to be gained by trying to close the resource from this line alone.
如果未找到该文件,则没有要关闭的基础资源,因此关闭它不会有任何区别。如果文件存在,它应该抛出一个 FileNotFoundException。因此,尝试仅从该行关闭资源没有任何好处。
The reason you need to close the file is when the file is opened successfully, but you later get an error.
您需要关闭文件的原因是文件成功打开时,但后来出现错误。
Lets look at the next stream GZIPOutputStream
让我们看看下一个流 GZIPOutputStream
There is code which can throw an exception
有可以抛出异常的代码
private void writeHeader() throws IOException {
out.write(new byte[] {
(byte) GZIP_MAGIC, // Magic number (short)
(byte)(GZIP_MAGIC >> 8), // Magic number (short)
Deflater.DEFLATED, // Compression method (CM)
0, // Flags (FLG)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Modification time MTIME (int)
0, // Extra flags (XFLG)
0 // Operating system (OS)
});
}
This writes the header of the file. Now it would be very unusual for you to be able to open a file for writing but not be able to write even 8 bytes to it, but lets imagine this could happen and we don't close the file afterwards. What does happen to a file if it is not closed?
这将写入文件的标题。现在,您能够打开一个文件进行写入但甚至无法向其中写入 8 个字节,这将是非常不寻常的,但是让我们想象一下这可能发生并且我们之后不会关闭该文件。如果文件没有关闭会怎样?
You don't get any unflushed writes, they are discarded and in this case, there is no successfully written bytes to the stream which isn't buffered at this point anyway. But a file which is not closed doesn't live forever, instead FileOutputStream has
你不会得到任何未刷新的写入,它们被丢弃,在这种情况下,没有成功写入流的字节,无论如何此时都没有缓冲。但是没有关闭的文件不会永远存在,而是 FileOutputStream
protected void finalize() throws IOException {
if (fd != null) {
if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
flush();
} else {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
}
If you don't close a file at all, it gets closed anyway, just not immediately (and like I said, data which is left in a buffer will be lost this way, but there is none at this point)
如果您根本不关闭文件,它无论如何都会关闭,只是不会立即关闭(就像我说的,留在缓冲区中的数据会以这种方式丢失,但此时没有)
What is the consequence of not closing the file immediately? Under normal conditions, you potentially lose some data, and you potentially run out of file descriptors. But if you have a system where you can create files but you can't write anything to them, you have a bigger problem. i.e. it hard to imagine why you are repeatedly trying to create this file despite the fact you are failing.
不立即关闭文件的后果是什么?在正常情况下,您可能会丢失一些数据,并且可能会用完文件描述符。但是,如果您有一个可以创建文件但不能向其中写入任何内容的系统,那么问题就更大了。即很难想象为什么尽管您失败了,但您仍反复尝试创建此文件。
Both OutputStreamWriter and BufferedWriter don't throw IOException in their constructors, so it not clear what problem they would cause. In The case of BufferedWriter, you could get an OutOfMemoryError. In this case it will immediately trigger a GC, which as we have seen will close the file anyway.
OutputStreamWriter 和 BufferedWriter 都不会在它们的构造函数中抛出 IOException,因此不清楚它们会导致什么问题。在 BufferedWriter 的情况下,您可能会收到 OutOfMemoryError。在这种情况下,它将立即触发 GC,正如我们所见,它无论如何都会关闭文件。
回答by Grzegorz ?ur
If all of the streams have been instantiated then closing only the outermost is just fine.
如果所有的流都被实例化了,那么只关闭最外面的流就可以了。
The documentation on Closeable
interface states that close method:
Closeable
接口上的文档指出关闭方法:
Closes this stream and releases any system resources associated with it.
关闭此流并释放与其关联的所有系统资源。
The releasing system resources includes closing streams.
释放系统资源包括关闭流。
It also states that:
它还指出:
If the stream is already closed then invoking this method has no effect.
如果流已经关闭,则调用此方法无效。
So if you close them explicitly afterwards, nothing wrong will happen.
因此,如果您之后明确关闭它们,则不会发生任何错误。
回答by Dmitry Bychenko
I'd rather use try(...)
syntax (Java 7), e.g.
我宁愿使用try(...)
语法(Java 7),例如
try (OutputStream outputStream = new FileOutputStream(createdFile)) {
...
}
回答by Sivakumar
In Java 7, there is a feature try-with-resources. You no need to explicitly close your streams, it will take care of that.
在 Java 7 中,有一个特性try-with-resources。您无需明确关闭您的流,它会处理这个问题。
回答by Codeversum
It will be fine if you only close the last stream - the close call will be send to the underlying streams, too.
如果您只关闭最后一个流就可以了 - 关闭调用也将发送到底层流。
回答by TheLostMind
No, the topmost level Stream
or reader
will ensure that all underlyingstreams / readers are closed.
不,最顶层Stream
或reader
将确保关闭所有底层流/读取器。
Check the close()
method implementationof your topmost level stream.
检查顶级流的close()
方法实现。