Recovering resources
Expensive resources should be reclaimed as soon as possible, by an explict call to a clean up method defined for this purpose. If this is not done, then system performance can degrade. In the worst cases, the system can even fail entirely.
Resources include :
- input-output streams
- database result sets, statements, and connections
- threads
- graphic resources
- sockets
For the case of a resource which is a field, however, there is more work to do :
- implement a clean up method which the user must call when finished with the object, with a name such as close or dispose
- the caller should be able to query an object to see if its clean up method has been executed
- non-private methods (other than the clean up method itself) should throw an IllegalStateException if the clean up method has already been invoked
- as a safety net, implement finalize 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 system
- never rely solely on finalize
import java.sql.*; import java.text.*; import java.util.*; /** * 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 (fIsDestroyed) { return; } else{ if (fConnection != null) fConnection.close(); fConnection = null; //flag that destory has been called, and that //no further calls on this object are valid fIsDestroyed = 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 aId) 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 fIsDestroyed; } // 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 fConnection; /** * This object has a specific "life cycle", such that methods must be called * in the order: others + destroy. fIsDestroyed 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 fIsDestroyed; /** * 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 ( fIsDestroyed ) { String message = "Method cannot be called after destroy has been called."; throw new IllegalStateException(message); } } }
See Also :
Always close streams
Never rely on finalize
Finally and catch
Get database connection
Always shut down an ExecutorService
Never rely on finalize
Finally and catch
Get database connection
Always shut down an ExecutorService
Would you use this technique?
|
|