try {
// Create a connection
Connection con = // ... create a connection // ...
Use connection object above, such as firing a query //
Clean up - release connection
con.close();
}
catch(SQLException e) {
e.printStackTrace();
}
The above program uses a Connection to perform some database operation.
It creates a connection, fires a query using it and finally, releases
the connection. This seemingly perfect piece of code has a loophole. As
you might have guessed, the code between creation and clean up of connection
object can generate an SQLException. If such a condition arises, the flow
will be directed to the catch block and the con.close() call will not
be executed. That means, the connection won't be closed!
Java provides a convenient way for dealing with these type of situations
by using a finally block. A finally block accompanies a try block. Remember
that a try block should be accompanied by a catch or a finally block or
both. One good thing of a finally block is that it's executed in both
the cases:
1. When the try block is exited because of the normal program flow
2. When the try block is exited because of an exception
When the try block is exited because of the normal flow, the corresponding
finally block will be executed before continuing. If an exception arises,
the appropriate catch block is executed first and then the finally block.
In case, an appropriate catch block could not be found and the exception
needs to be propagated up the caller stack, the finally block is executed
before doing so. That means, in any case, the finally block is executed.
This property of the finally block can be leveraged to perform clean up.
Here's the modified version of the above program:
Connection con = null;
try {
// Create a connection
con = //... create a connection
//... Use connection object above, such as firing a query
// Clean up - release connection
con.close();
}
catch(SQLException ex) {
ex.printStackTrace();
}
finally{
// Clean up
try {
if (con != null) {
con.close(); }
}
catch(SQLException sqlEx)
{ //... Ignore exception
}
}
As you can see, there are simple changes. But, the clean up is now being
done in a better way. First of all, we have moved the declaration for
the connection object, con, outside the try block, so that it's visible
in the finally block. Rest of the try block is the same as previously.
In the finally block we are doing a couple of interesting things. Firstly,
we have enclosed the clean up code in a try block. Since there's nothing
much we can do if we get an exception while closing a connection, we are
just ignoring it. This would suffice in most of the cases, unless you
want to deal with the situation (such as logging the error). Secondly,
we are checking if the connection object is not null. This is important
because the exception might have occured at the time of creating a connection.
At that time, con would be null. If we remove the if check, it would lead
to a NullPointerException. And finally, we are closing the connection
object.
For clean up, some people like to use finalize(), which is a special
method that's invoked by the garbage collector just before the object
is garbage collected. However, garbage collector may not even get a chance
to run. So, you cannot really rely on finalize() to perform clean up.
As I mentioned earlier, performing clean up is an important aspect of
developing code. And care should be taken to ensure that the clean up
is performed in all possible cases. This makes your program more robust
and at the same time well-behaved.