Java에서 중첩 된 스트림 및 작성자를 닫는 올바른 방법
이 질문에 이미 답변이 있습니다.
참고 : 이 질문과 대부분의 답변은 Java 7이 출시되기 이전의 날짜입니다. Java 7 은이 작업을 쉽게 수행 할 수있는 자동 자원 관리 기능을 제공합니다 . Java 7 이상을 사용하는 경우 Ross Johnson의 답변으로 진행해야합니다 .
Java에서 중첩 스트림을 닫는 가장 좋고 포괄적 인 방법은 무엇입니까? 예를 들어 다음 설정을 고려하십시오.
FileOutputStream fos = new FileOutputStream(...)
BufferedOS bos = new BufferedOS(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
닫기 작업에 보험이 필요하다는 것을 이해합니다 (아마 finally 절을 사용하여). 내가 궁금한 것은 중첩 된 스트림이 닫혔는지 명시 적으로 확인해야 하는가 아니면 외부 스트림 (oos)을 닫는 것만으로도 충분합니까?
적어도이 특정 예제를 다루면서 내가 알아 차린 한 가지는 내부 스트림이 FileNotFoundExceptions 만 던지는 것처럼 보인다는 것입니다. 이는 실패 할 경우 닫는 것에 대해 기술적으로 걱정할 필요가 없음을 의미하는 것 같습니다.
동료가 쓴 내용은 다음과 같습니다.
기술적으로 제대로 구현 되었다면 가장 바깥 쪽 스트림 (oos)을 닫는 것으로 충분합니다. 그러나 구현에 결함이있는 것 같습니다.
예 : BufferedOutputStream은 FilterOutputStream에서 close ()를 상속하며 다음과 같이 정의합니다.
155 public void close() throws IOException {
156 try {
157 flush();
158 } catch (IOException ignored) {
159 }
160 out.close();
161 }
그러나 flush ()가 어떤 이유로 런타임 예외를 던지면 out.close ()가 호출되지 않습니다. 따라서 파일을 계속 열어 두는 FOS를 닫는 것에 대해 대부분 걱정하는 것이 "가장 안전"(하지만 못생긴) 것처럼 보입니다.
중첩 스트림을 닫기 위해 절대적으로 필요한 경우 최선의 방법으로 간주되는 것은 무엇입니까?
그리고 이것을 자세히 다루는 공식 Java / Sun 문서가 있습니까?
나는 보통 다음을한다. 먼저 try / catch 엉망을 처리하기위한 템플릿 메서드 기반 클래스를 정의합니다.
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);
}
}
}
}
}
"pending"예외에주의하십시오. 이것은 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
}
Using this approach you never have to worry about the try/catch/finally to deal with closing files again.
If this is too heavy for your use, at least think about following the try/catch and the "pending" variable approach it uses.
When closing chained streams, you only need to close the outermost stream. Any errors will be propagated up the chain and be caught.
Refer to Java I/O Streams for details.
To address the issue
However, if flush() throws a runtime exception for some reason, then out.close() will never be called.
This isn't right. After you catch and ignore that exception, execution will pick back up after the catch block and the
out.close()
statement will be executed.
Your colleague makes a good point about the RuntimeException. If you absolutely need the stream to be closed, you can always try to close each one individually, from the outside in, stopping at the first exception.
In the Java 7 era, try-with-resources is certainly the way to go. As mentioned in several previous answers, the close request propagates from the outermost stream to the innermost stream. So a single close is all that is required.
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
// do something with ois
}
There is however a problem with this pattern. The try-with-resources is not aware of the inner FileInputStream, so if the ObjectInputStream constructor throws an exception, the FileInputStream is never closed (until the garbage collector gets to it). The solution is...
try (FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis)) {
// do something with ois
}
This is not as elegant, but is more robust. Whether this is actually a problem will depend on what exceptions can be thrown during construction of the outer object(s). ObjectInputStream can throw IOException which may well get handled by an application without terminating. Many stream classes only throw unchecked exceptions, which may well result in termination of the application.
It is a good practice to use Apache Commons to handle IO related objects.
In the finally
clause use IOUtils
IOUtils.closeQuietly(bWriter); IOUtils.closeQuietly(oWritter);
Code snippet below.
BufferedWriter bWriter = null;
OutputStreamWriter oWritter = null;
try {
oWritter = new OutputStreamWriter( httpConnection.getOutputStream(), "utf-8" );
bWriter = new BufferedWriter( oWritter );
bWriter.write( xml );
}
finally {
IOUtils.closeQuietly(bWriter);
IOUtils.closeQuietly(oWritter);
}
The colleague raises an interesting point, and there are grounds for arguing either way.
Personally, I would ignore the RuntimeException
, because an unchecked exception signifies a bug in the program. If the program is incorrect, fix it. You can't "handle" a bad program at runtime.
This is a surprisingly awkward question. (Even assuming the acquire; try { use; } finally { release; }
code is correct.)
If the construction of the decorator fails, then you wont be closing the underlying stream. Therefore you do need to close the underlying stream explicitly, whether in the finally after use or, more diifcult after successfully handing over the resource to the decorator).
If an exception causes execution to fail, do you really want to flush?
Some decorators actually have resources themselves. The current Sun implementation of ZipInputStream
for instance has non-Java heap memory allocated.
It has been claimed that (IIRC) two thirds of the resources uses in the Java library are implemented in a clearly incorrect manner.
Whilst BufferedOutputStream
closes even on an IOException
from flush
, BufferedWriter
closes correctly.
My advice: Close resources as directly as possible and don't let them taint other code. OTOH, you can spend too much time on this issue - if OutOfMemoryError
is thrown it's nice to behave nicely, but other aspects of your program are probably a higher priority and library code is probably broken in this situation anyway. But I'd always write:
final FileOutputStream rawOut = new FileOutputStream(file);
try {
OutputStream out = new BufferedOutputStream(rawOut);
... write stuff out ...
out.flush();
} finally {
rawOut.close();
}
(Look: No catch!)
And perhaps use the Execute Around idiom.
The Java SE 7 try-with-resources doesn't seem to be mentioned. It eliminates needing to explicitly do a close completely, and I quite like the idea.
Unfortunately, for Android development this sweet only becomes available by using Android Studio (I think) and targeting Kitkat and above.
Also you dont have to close all nested streams
check this http://ckarthik17.blogspot.com/2011/02/closing-nested-streams.html
I use to close streams like this, without nesting try-catch in finally blocks
public class StreamTest {
public static void main(String[] args) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(new File("..."));
bos = new BufferedOutputStream(fos);
oos = new ObjectOutputStream(bos);
}
catch (Exception e) {
}
finally {
Stream.close(oos,bos,fos);
}
}
}
class Stream {
public static void close(AutoCloseable... array) {
for (AutoCloseable c : array) {
try {c.close();}
catch (IOException e) {}
catch (Exception e) {}
}
}
}
Sun's JavaDocs include RuntimeException
s in their documentation, as shown by InputStream's read(byte[], int, int) method; documented as throwing NullPointerException and IndexOutOfBoundsException.
FilterOutputStream's flush() is only documented as throwing IOException, thus it doesn't actually throw any RuntimeException
s. Any that could be thrown would most likely be wrapped in an IIOException
.
It could still throw an Error
, but there's not much you can do about those; Sun recommends that you don't try to catch them.
참고URL : https://stackoverflow.com/questions/884007/correct-way-to-close-nested-streams-and-writers-in-java
'IT story' 카테고리의 다른 글
무료 NCrunch 대안 (0) | 2020.09.01 |
---|---|
HttpSecurity, WebSecurity 및 AuthenticationManagerBuilder (0) | 2020.09.01 |
HTML 링크로 폴더를 여는 방법은 무엇입니까? (0) | 2020.09.01 |
API 설계를위한 모범 사례 및 지침 (0) | 2020.09.01 |
AngularJS의 ng-repeat 루프 내부에 ng-model 바인딩 (0) | 2020.09.01 |