Always close streams
Never rely on finalize
Finally and catch
Get database connection
Always shut down an ExecutorService
Resources include:
close
or dispose
. (The exact name of the method
is arbitrary, but it usually has those conventional names.)
This is usually done automatically, using the
try-with-resources
feature, added in JDK 7.
If try-with-resources isn't available, then you need to clean up resources explicitly, by calling a clean-up
method in a finally
clause.
For the case of a resource which is a field, however, there's more work to do:
close
or dispose
private
methods (other than the clean-up method itself) should
throw an IllegalStateException
if the clean-up method has already
been invokedfinalize
to call the clean-up method
as well; if the user of the class neglects to call the clean-up method,
then this may allow recovery of the resource by the systemfinalize
import java.sql.*; /** * This class has an enforced life cycle: after destroy is * called, no useful method can be called on this object * without throwing an IllegalStateException. */ public final class DbConnection { public DbConnection () { //build a connection and assign it to a field //elided.. fConnection = ConnectionPool.getInstance().getConnection(); } /** * Ensure the resources of this object are cleaned up in an orderly manner. * * The user of this class must call destroy when finished with * the object. Calling destroy a second time is permitted, but is * a no-operation. */ public void destroy() throws SQLException { if (isDestroyed) { return; } else{ if (connection != null) connection.close(); connection = null; //flag that destory has been called, and that //no further calls on this object are valid isDestroyed = true; } } /** * Fetches something from the db. * * This is an example of a non-private method which must ensure that * <code>destroy</code> has not yet been called * before proceeding with execution. */ synchronized public Object fetchBlah(String id) throws SQLException { validatePlaceInLifeCycle(); //..elided return null; } /** * If the user fails to call <code>destroy</code>, then implementing * finalize will act as a safety net, but this is not foolproof. */ protected void finalize() throws Throwable{ try{ destroy(); } finally{ super.finalize(); } } /** * Allow the user to determine if <code>destroy</code> has been called. */ public boolean isDestoyed() { return isDestroyed; } // PRIVATE /** * Connection which is constructed and managed by this object. * The user of this class must call destroy in order to release this * Connection resource. */ private Connection connection; /** * This object has a specific "life cycle", such that methods must be called * in the order: others + destroy. isDestroyed keeps track of the lifecycle, * and non-private methods must check this value at the start of execution. * If destroy is called more than once, a no-operation occurs. */ private boolean isDestroyed; /** * Once <code>destroy</code> has been called, the services of this class * are no longer available. * * @throws IllegalStateException if <code>destroy</code> has * already been called. */ private void validatePlaceInLifeCycle(){ if (isDestroyed) { String message = "Method cannot be called after destroy has been called."; throw new IllegalStateException(message); } } }