How to properly handle an IOException from close()

2019-03-15 02:14发布

The Java I/O classes java.io.Reader, java.io.Writer, java.io.InputStream, java.io.OutpuStream and their various subclasses all have a close() method that can throw an IOException.

Is there any consensus on the proper way to handle such exceptions?

I have often seen recommendations to just silently ignore them, but that feels wrong, and at least in case of resources opened for writing, a problem while closing the file might mean that unflushed data could not be written/sent.

On the other hand, when reading resources, I'm totally unclear on why close() might throw and what to do about it.

So is there any standard recommendation?

A related question is Does close ever throw an IOException?, but that is more about which implementations really do throw, not about how to handle the exceptions.

9条回答
一纸荒年 Trace。
2楼-- · 2019-03-15 02:30

It depends on what you are closing. For example, closing a StringWriter does nothing. The javadocs are clear on that. In this case, you can ignore the IOException because you know that it will never be generated. Technically, you don't even need to call close.

/**
 * Closing a <tt>StringWriter</tt> has no effect. The methods in this
 * class can be called after the stream has been closed without generating
 * an <tt>IOException</tt>.
 */
public void close() throws IOException {
}

For other streams, log and handle the exception as appropriate.

查看更多
Juvenile、少年°
3楼-- · 2019-03-15 02:30

Generally resource handling should look like this:

final Resource resource = context.acquire();
try {
    use(resource);
} finally {
    resource.release();
}

In (the unlikely) case of release throwing an exception, any exception thrown by use will be dropped. I don't have a problem with the exception thrown closest to the catcher winning. I believe ARM blocks in JDK7(?) will do something crazy in this case, like rethrowing the use exception with the release exception attached.

If you use the Execute Around idiom, you can put these decisions and potentially messy code in one (or few) places.

查看更多
看我几分像从前
4楼-- · 2019-03-15 02:31

Ignoring or just logging is usually a bad idea. Even though it is true that you cannot recover from it, I/O exceptions should always be handled in a unified manner to make sure your software behaves in a unified way.

I would at least suggest handling like below:

//store the first exception caught
IOException ioe = null;
Closeable resource = null;
try{
  resource = initializeResource();
  //perform I/O on resource here
}catch(IOException e){
  ioe = e;
}finally{
  if (resource != null){
    try{
      resource.close();
    }catch(IOException e){
      if(null == ioe){
        //this is the first exception
        ioe = e;
      }else{
        //There was already another exception, just log this one.
        log("There was another IOException during close. Details: ", e);
      }
    }
  }
}

if(null != ioe){
  //re-throw the exception to the caller
  throw ioe;
}

The above is pretty verbose but works well, because it will prefer the IOException during I/O on the resource to the one during close, since it is more likely to contain information of interest to the developer. The code will also throw an IOException when something goes wrong, so you get a unified behaviour.

There might be nicer ways of doing it, but you get the idea.

Ideally, you would create a new exception type that would allow storing sibling exceptions, so in case you get two IOExceptions you could store them both.

查看更多
5楼-- · 2019-03-15 02:34

Try to use flush before you close the writer. The main reason for exception in close is that some other resources might have been using the data or the writer/reader may not be open at all. try to find wheather the resoures is open before closing it.

查看更多
\"骚年 ilove
6楼-- · 2019-03-15 02:39

First of all don't forget to put the close() inside the "finally" section of your try catch block. Second you can surround the close method with another try catch exception and log the exception.

查看更多
▲ chillily
7楼-- · 2019-03-15 02:45

Well, in most cases, close() doesn't actually throw an IOException. Here's the code for InputStream.java:

  public void close() throws IOException
  {
    // Do nothing
  }

Errors from closing a network resource should really be of some type of RuntimeException, since you can disconnect a networked resource after the program connects to it.

You can see some example of various implementations of Reader/Writer and Streams using Google Code Search. Neither BufferedReader nor PipedReader actually throw an IOException, so I think you're mostly in the safe by not worrying about it. If you're really worried, you can check the implementation of the libraries you're using to see if you ever need to worry about the exception.

Like others mentioned, you can't do much about the IOException other than log it.

After all, try/catch blocks in the finally clause are pretty ugly.

Edit:

Further inspection reveals subclasses of IOException like InterruptedIOException, SyncFailedException, and ObjectStreamException, along with classes that inherit from it. So just catching an IOException would be too general -- you wouldn't know what to do with the information other than logging it because it could be from a whole range of errors.

Edit 2:

Urk, BufferedReader was a bad example, since it takes a Reader as input. I've changed it to InputStream.java

However, there's a hierarchy with InputStream <= FilterInputStream <= BufferedInputStream <= InputStreamReader (via inheritance and private instances) that all trickle up to the close() method in InputStream.

查看更多
登录 后发表回答