The Java I/O classes java.io.Reader
, java.io.Writer
, java.io.InputStream
, java.io.OutpuStream
and their various subclasses al
Your code that calls InputStream.close()
or OutputStream.close()
is some high level code that delegates to the lower level InputStream
or OutputStream
class to perform some high level computation.
You have only 3 choices about what to do.
The last two options are similar in that your code throws an exception. So this question is actually a special case of the question of when should my code throw an exception. The correct answer to that question in this context is: if, and only if, the alternative, is to fail to meet a post condition of your code or to maintain an invariant of your code. The post conditions specify what your method is meant to do. The invariants specify characteristics of the class.
So, you need to ask yourself whether the close()
throwing an exception prevents your method doing what is should do.
close()
throwing does prevent your method doing its job, and the method may throw IOException
, you can do nothing, just letting the exception propagate upwards.close()
prevents your method doing its job, but the method may not throw IOException
, you must catch the IOException
and rethrow it as a different class of exception, recording the IOException
as the cause of the thrown exception.I know of no circumstances in which a InputStream.close()
throwing an exception can prevent a computation succeeding. That call to close()
naturally happens after you have finished reading whatever data you are interested in.
Output streams, however, can buffer data to be written to the output destination, either internally (within the Java code) or at a lower level (within the operating system). You therefore can not be sure that write operations to an output stream have actually resulted in real output until OutputStream.close()
has successfully returned (without throwing an exception). You should therefore treat an exception thrown by OutputStream.close()
just like a write failure.
Your method is responsible for maintaining its invariants. Sometimes this will require a clean-up or roll-back operation if close()
throws an exception. You should put the code for that in a catch
or finally
clause for the IOException
, even if you would like the exception to propagate upwards. If you use a catch clause and you want to propagate it, you will have to rethrow it.
Some people will advise you to log the exception. This is almost always bad advice. Log messages are part of the user interface of your program. Fundamentally, you must always ask yourself whether to log anything at all, because useless verbiage can distract and confuse the users reading your log file ("users" includes system administrators). Every message logged should be for some useful purpose. Each should provide information that helps the user make decisions.
Reporting specifically that close()
failed is rarely useful. How can it help a user make a decision? If the exception did not prevent your method from doing its job, there is no problem, and no actions by the user are necessary. If your program failed to close()
the stream, and that is a problem, what can the user do to help?
Low level code is not usually responsible for logging at all. It instead performs abstract operations, reporting failure to higher level parts of your program by throwing exceptions. Code closing a stream is typically rather low level, so the code that detects that close()
is too low level to do any logging.
The specific fact that close()
failed is rarely useful. What can be useful is knowing that the abstract operation that your method is meant to perform failed. That can be done by having the higher level code catch all expected exceptions and reporting that the operation failed, rather than having your method precisely report that "close failed".