Ado*_*ith 126 java file-io outputstream writer
我正在写一段代码:
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));
Run Code Online (Sandbox Code Playgroud)
我是否需要关闭以下每个流或作者?
gzipOutputStream.close();
bw.close();
outputStream.close();
Run Code Online (Sandbox Code Playgroud)
或者只关闭最后一个流好吗?
bw.close();
Run Code Online (Sandbox Code Playgroud)
T.J*_*der 146
假设所有的流获得创建好了,是的,刚刚闭幕bw的精细与流实现 ; 但这是一个很大的假设.
我使用try-with-resources(教程),以便构造抛出异常的后续流的任何问题都不会使先前的流挂起,因此您不必依赖关闭调用的流实现底层流:
try (
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
请注意,您根本不再打电话close.
重要说明:要使用try-with-resources关闭它们,必须在打开它们时将流分配给变量,不能使用嵌套.如果使用嵌套,则在构造其中一个后续流(例如,GZIPOutputStream)时会出现异常,这将使嵌套调用构建的任何流都打开.来自JLS§14.20.3:
try-with-resources语句使用变量(称为资源)进行参数化,这些变量在执行
try块之前初始化,并在执行try块之后以与它们初始化的相反顺序自动关闭.
注意"变量"这个词(我的重点).
例如,不要这样做:
// DON'T DO THIS
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new GZIPOutputStream(
new FileOutputStream(createdFile))))) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
...因为GZIPOutputStream(OutputStream)构造函数的异常(表示它可能会抛出IOException,并将标题写入底层流)将保持FileOutputStream开放状态.由于某些资源具有可能抛出的构造函数而其他资源没有,因此将它们单独列出是一个好习惯.
我们可以用这个程序仔细检查我们对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");
}
}
Run Code Online (Sandbox Code Playgroud)
...有输出:
Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost In catch block In finally block At end of main
请注意,那里没有电话close.
如果我们修复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");
}
Run Code Online (Sandbox Code Playgroud)
然后我们得到适当的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
(是的,两个调用InnerMost#close是正确的;一个来自Middle,另一个来自try-with-resources.)
Pet*_*rey 12
您可以关闭最外层的流,实际上您不需要保留所有包裹的流,您可以使用Java 7 try-with-resources.
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new GZIPOutputStream(new FileOutputStream(createdFile)))) {
// write to the buffered writer
}
Run Code Online (Sandbox Code Playgroud)
如果你订阅YAGNI,或者你不需要它,你应该只添加你真正需要的代码.您不应该添加您可能需要的代码,但实际上并没有做任何有用的事情.
以这个例子为例,想象如果你不这样做会出现什么问题,影响会是什么?
try (
OutputStream outputStream = new FileOutputStream(createdFile);
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream);
OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream);
BufferedWriter bw = new BufferedWriter(osw)
) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
让我们从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;
Run Code Online (Sandbox Code Playgroud)
如果找不到该文件,则没有要关闭的底层资源,因此关闭它不会有任何区别.如果文件存在,则应抛出FileNotFoundException.因此,尝试单独关闭此行的资源无法获得任何好处.
您需要关闭文件的原因是文件成功打开,但您稍后会收到错误.
让我们看看下一个流 GZIPOutputStream
有代码可以抛出异常
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)
});
}
Run Code Online (Sandbox Code Playgroud)
这会写入文件的标头.现在,你能够打开一个文件进行写入但是甚至不能写入8个字节是非常不寻常的,但是让我们想象这可能会发生,之后我们不会关闭文件.如果文件未关闭,会发生什么?
你没有获得任何未刷新的写入,它们被丢弃,在这种情况下,没有成功写入流的字节,此时无法缓冲.但是一个未关闭的文件不会永远存在,而是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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果你根本没有关闭文件,它就会被关闭,只是没有立即关闭(就像我说的那样,留在缓冲区中的数据会以这种方式丢失,但此时没有)
不立即关闭文件的后果是什么?在正常情况下,您可能会丢失一些数据,并且可能会耗尽文件描述符.但是如果你有一个系统,你可以创建文件,但你不能写任何东西,你有一个更大的问题.即使您失败,很难想象为什么你会反复尝试创建这个文件.
OutputStreamWriter和BufferedWriter都不会在其构造函数中抛出IOException,因此不清楚它们会导致什么问题.在BufferedWriter的情况下,您可能会得到一个OutOfMemoryError.在这种情况下,它将立即触发GC,正如我们所见,无论如何都会关闭文件.
我宁愿使用try(...)语法(Java 7),例如
try (OutputStream outputStream = new FileOutputStream(createdFile)) {
...
}
Run Code Online (Sandbox Code Playgroud)