Question

I'm studying the online java course, Introduction to programming Using Java.

In the chapter on I/O the code below is introduced with the following statement:

By the way, at the end of this program, you'll find our first useful example of a finally clause in a try statement. When the computer executes a try statement, the commands in its finally clause are guaranteed to be executed, no matter what.

The program is at the end of section 11.2.1 and is a simple program that just reads some numbers in from a file and writes them in reverse order.

The relevant code in the main method is (data is a Reader and result is a Writer):

try {
    // Read numbers from the input file, adding them to the ArrayList.        
    while ( data.eof() == false ) {  // Read until end-of-file.
        double inputNumber = data.getlnDouble();
        numbers.add( inputNumber );
    }

    // Output the numbers in reverse order.        
    for (int i = numbers.size()-1; i >= 0; i--)
        result.println(numbers.get(i));

    System.out.println("Done!");        
} catch (IOException e) {
    // Some problem reading the data from the input file.
    System.out.println("Input Error: " + e.getMessage());
} finally {
    // Finish by closing the files, whatever else may have happened.
    data.close();
    result.close();
}

So I was wondering why the finally clause was useful in this case when there are no other exit points from the try or catch clauses. Couldn't the close methods just be in the body of main instead?

I thought maybe it was because there could theoretically be some other RuntimeException which could crash the program and then leave the Reader & Writers unclosed, but then wouldn't the fact that the program had crashed close them anyway?

Was it helpful?

Solution

Your idea is correct: The finally block will close the resources even if an unexpected exception ocurred.

You are also right that this is irrelevant if such an exception crashes the complete application, but from looking at this code you can't decide if this is the case. There might be other exception handlers, catching that exception, so it is good and correct practice to put the closing logic in a finally block.

Note that there still might be a bug hiding: if data.close() throws an exception, result.close() will never get called.

Depending on your environment there are various flavours on how to fix the bug.

  • in java 7 ff you can use try-with-resources

  • if you are using Spring, there might be a proper template similar to the JdbcTemplate

  • if nothing of those applies, yes you'll have to do a try/finally inside the finally. Quit ugly. You absolutely should at least extract this into a method as suggested in the comments.

  • conceptually cleaner but rather wordy in java pre 8 is to implement the loan pattern. If you don't happen to work with scala/clojure/haskell developers it might be more confusing then anything else.

OTHER TIPS

This is for a very simple reason: it is the safest way, pre Java 7 and try-with-resources, to guarantee that your resources are closed even if an exception is caught.

Consider what happened if you did instead:

try {
    // some code, then

    resource.close();
} catch (SomeException e) {
    // etc
}

If your SomeException is thrown before the resource is closed, you can leak resources. Putting the resource.close() into finally, on the other hand, guarantees that it gets closed no matter what else happens.

With Java 7 you would use this:

try (
    final InputStream in = Files.newInputStream(Paths.get("somefile"));
    // Others
) {
    // work with "in" and others
} catch (Whatever e) {
}

Your resources would then be closed before catch.


As a side note, using Java 6, the safest way to have your resources closed is to use Guava's Closer.

If the program terminated after that, then yes, that would also close the I/O resources.

But many programs don't terminate. There are some which must run 24/7 for years. So cleaning up your resources properly is a must-have.

Unfortunately, Java < 7 only offers an automatic cleanup mechanism for memory (garbage collection). With Java 7, you get the new "try-with-resource statement" that tries to plug the hole.

Unless you can use this version, you have to do the cleanup yourself.

That said, the code above is still buggy: close() can itself throw an exception so some of the I/O resources might still stay around. You should use tools like IOUtils.closeQuietly() instead:

Reader reader = null;
try {
    reader = ...open...

    ...use reader...
} finally {
    IOUtils.closeQuietly(reader);
}

From Java doc:

The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

And about your concerns about:

the fact that the program had crashed close them anyway.

The resources allocate in the OS level, not at your program, so if your program haven't chance to clean up, then the resources will be allocated with out really use.

True. If RuntimeException occurs, finally will get executed thus closing resources and this is not theoretically. This is a practical scenario.

Further, even if IOException (or many others you have caught) occurs. finally clause prevents you from writing same code for closing many times.

Finally ensures a piece of code is always called. Because of that, it's the best place to close connections. I would advise to put your close statements in a try catch as well, since something may still go wrong in the finally block:

finally {
    if (data != null) try { data.close() } catch(exception ex) { ex.printstacktrace() }
}

That is actually a right concept you've formed. When a CheckedException like IOException occurs, then all the resources must be closed. This is because:

  1. When you were executing the program, the resources were being accessed. Supposing you had changed a few things and you didn't saved them, you wouldn't get the updated file. (This happens when you use Buffers - They do not write the data instantaneously, they write in chunks).

  2. Since the File was open in the JVM, there's a probability that it'll stay open and you will need to close the application youre using. Hence, you need to close() the resources, so that the buffers are flushed i.e. changes are saved.

For example:

try {
    BufferedReader br = new BufferredReader (new FileReader("Example.txt"));

    ArrayList<String> lines = new ArrayList<>();
    String line;
    while ( (line = br.readLine()) != null ) {
        lines.add(line);
    }
catch (IOException ie) {
    //Error handling.
} finally {
    br.close();
}

EDIT: With the advent of JDK1.7, now you can use try with resources, shown as follows:

try (BufferedReader br = new BufferredReader (new FileReader("Example.txt"));) {
    ArrayList<String> lines = new ArrayList<>();
    String line;
    while ( (line = br.readLine()) != null ) {
        lines.add(line);
    }
catch (IOException ie) {
    //Error handling.
}

Now, the finally block is not need because BufferedReader implements AutoCloseable. As the name suggests, it automatically closes the buffer when try (..) block is left.

In the case where an exception is thrown or not thrown, the finally clause will ensure that both data and result streams are closed. Otherwise, they may not be closed.

A "close" method can do more than simply notify an operating system that a resource is no longer required. Among other things, objects which encapsulate various forms of data streams may buffer a certain amount of data themselves, and pass it along to the operating system either when they are closed or when a certain amount of data has been accumulated. Having an object which encapsulates a stream of log data pass along data to the OS in large blocks may be much more efficient than having it pass log events to the OS individually, but if the program dies without "closing" the log file, information which would likely be very relevant to anyone diagnosing the problem will be lost.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top