Java try/catch/finally best practices while acquiring/closing resources

后端 未结 8 1056
隐瞒了意图╮
隐瞒了意图╮ 2020-11-27 13:43

While working on a school project, I wrote the following code:

FileOutputStream fos;
ObjectOutputStream oos;
try {
    fos = new FileOutputStream(file);
             


        
相关标签:
8条回答
  • 2020-11-27 14:20

    Java 7 will add Automatic Resource Management blocks. They are very similar to C#'s using.

    Josh Bloch wrote the technical proposal, which I highly recommend reading. Not just because it will give you a leg up on an upcoming Java 7 language feature, but because the specification motivates the need for such a construct, and in doing so, illustrates how to write correct code even in the absence of ARM.

    Here's an example of the Asker's code, translated into ARM form:

    try (FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos)) 
    {
        oos.writeObject(shapes);
    }
    catch (FileNotFoundException ex) 
    {
        // handle the file not being found
    }
    catch (IOException ex) 
    {
        // handle some I/O problem
    }
    
    0 讨论(0)
  • 2020-11-27 14:21

    How about this guys? No null check, no surprise. Everything is cleaned upon exit.

    try {
        final FileOutputStream fos = new FileOutputStream(file);
        try {
            final ObjectOutputStream oos = new ObjectOutputStream(fos);
            try {
                oos.writeObject(shapes);
                oos.flush();
            }
            catch(IOException ioe) {
                // notify user of important exception
            }
            finally {
                oos.close();
            }
        }
        finally {
            fos.close();
        }
    }
    catch (FileNotFoundException ex) {
        // complain to user
    }
    catch (IOException ex) {
        // notify user
    }
    
    0 讨论(0)
  • 2020-11-27 14:24

    I usually have small class IOUtil with method such as:

    public static void close(Closeable c) {
        if (c != null) {
            try {
                c.close();
            }
            catch (IOException e) {
                // ignore or log
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-27 14:27

    Current best practice for try/catch/finally involving objects that are closeable (e.g. Files) is to use Java 7's try-with-resource statement, e.g.:

    try (FileReader reader = new FileReader("ex.txt")) {
        System.out.println((char)reader.read());
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
    

    In this case, the FileReader is automatically closed at the end of the try statement, without the need to close it in an explicit finally block. There are a few examples here:

    http://ppkwok.blogspot.com/2012/11/java-cafe-2-try-with-resources.html

    The official Java description is at:

    http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html

    0 讨论(0)
  • 2020-11-27 14:35

    If you are trying to catch and report all exceptions at source, a better solution is this:

    ObjectOutputStream oos = null;
    try {
       oos = new ObjectOutputStream(new FileOutputStream(file));
       oos.writeObject(shapes);
       oos.flush();
    } catch (FileNotFoundException ex) {
        // complain to user
    } catch (IOException ex) {
        // notify user
    } finally {
        if (oos != null) {
            try {
                oos.close();
            } catch (IOException ex) {
                // ignore ... any significant errors should already have been
                // reported via an IOException from the final flush.
            }
        }
    }
    

    Notes:

    • The standard Java wrapper streams, readers and writers all propagate close and flush to their wrapped streams, etc. So you only need to close or flush the outermost wrapper.
    • The purpose of flushing explicitly at the end of the try block is so that the (real) handler for IOException gets to see any write failures1.
    • When you do a close or flush on an output stream, there is a "once in a blue moon" chance that an exception will be thrown due to disc errors or file system full. You should not squash this exception!.

    If you often have to "close a possibly null stream ignoring IOExceptions", then you could write yourself a helper method like this:

    public void closeQuietly(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException ex) {
                // ignore
            }
        }
    }
    

    then you can replace the previous finally block with:

    } finally {
        closeQuietly(oos);
    }
    

    (Another answer points out that a closeQuietly method is already available in an Apache Commons library ... if you don't mind adding a dependency to your project for a 10 line method. UPDATE : note that these methods are deprecated in version 2.6 of the API.)

    But be careful that you only use closeQuietly on streams where IO exceptions really are irrelevant.

    1 - That is not necessary when using try-with-resources.


    On the issue of flush() versus close() that people are asking about:

    • The standard "filter" and "buffered" output streams and writers have an API contract that states that close() causes all buffered output to be flushed. You should find that all other (standard) output classes that do output buffering will behave the same way. So, for a standard class it is redundant to call flush() immediately before close().
    • For custom and 3rd-party classes, you need to investigate (e.g. read the javadoc, look at the code), but any close() method that doesn't flush buffered data is arguably broken.
    • Finally, there is the issue of what flush() actually does. What the javadoc says is this (for OutputStream ...)

      If the intended destination of this stream is an abstraction provided by the underlying operating system, for example a file, then flushing the stream guarantees only that bytes previously written to the stream are passed to the operating system for writing; it does not guarantee that they are actually written to a physical device such as a disk drive.

      So ... if you hope / imagine that calling flush() guarantees that your data will persist, you are wrong! (If you need to do that kind of thing, look at the FileChannel.force method ...)


    On the other hand, if you can use Java 7 or later, the "new" try-with-resources as described in @Mike Clark's answer is the best solution.

    If you are not using Java 7 or later for your new code, you are probably in a deep hole and digging deeper.

    0 讨论(0)
  • 2020-11-27 14:40

    Not a direct answer to your point but it is an unfortunate fact that because finally and catch are both associated with try people think that they belong together. The best design for try blocks is to either have a catch or a finally but not both.

    In this case your comments hint that something is wrong. Why, in a method dealing with file IO, are we complaining about anything to the user. We might be running deep on a server somewhere with nary a user in sight.

    So, the code you present above should have a finally to fail gracefully when things go wrong. It lacks the capacity to deal intelligently with errors however, so your catch belongs somewhere higher up on the call chain.

    0 讨论(0)
提交回复
热议问题