I'm reading Effective Java by Joshua Bloch. In ITEM 8: AVOID FINALIZERS AND CLEANERS of CHAPTER 2, he highlights the problems with using finalizers and cleaners for memory management. This article by Baeldung gives a good theoretical explanation of what finalizers entail. And an answer by Holger to my previous question excellently demonstrates the problems with the finalize()
, particularly for security.
As a remedy for circumventing finalizers and cleaners, using AutoCloseable
is recommended in the book:
So what should you do instead of writing a finalizer or cleaner for a class whose objects encapsulate resources that require termination, such as files or threads? Just have your class implement
AutoCloseable
, and require its clients to invoke theclose
method on each instance when it is no longer needed, typically usingtry
-with-resources to ensure termination even in the face of exceptions (Item 9).
However, he adds an ancillary point that we must keep track of whether the object has been closed, stating:
One detail worth mentioning is that the instance must keep track of whether it has been closed: the close method must record in a field that the object is no longer valid, and other methods must check this field and throw an
IllegalStateException
if they are called after the object has been closed.
Unfortunately, he hasn't given a tangible example in code of how to do this when writing an API. I would really appreciate it if you could do so?
My research on this matter so far has been to no avail. One of the top-rated questions on this I found had an answer that suggested an alternative method of freeing the client of this responsibility which perhaps is a better solution but still doesn't answer the question I have pertaining to what Joshua Bloch has stated.
Thanks a lot!
Thanks to ndc85430 and Louis Wasserman, I managed to put together a working example. N.B. I repurposed Baeldung's demo.
/** Example courtesy of Baeldung
* Refer to unit test to see the client implementation.
*/
public class DemoClass implements AutoCloseable {
/** Reads text from an input stream. */
private BufferedReader reader;
/** Flag that keeps track if resource has been closed. */
boolean closed = false;
/**
* An object created from the Finalisable class
* will create a new BufferedReader instance
* that reads from the specified classpath.
*/
public DemoClass() throws IOException {
File initialFile = new File("src/main/java/Item8/finalisers/nietzsche.txt");
InputStream input = new FileInputStream(initialFile);
reader = new BufferedReader(new InputStreamReader(input));
}
/** Reads first line from the text stream.*/
public String readFirstLine() throws IOException {
/** Checks if the resource is closed prior to accessing it. */
if (closed) throw new IllegalStateException();
return reader.readLine();
}
@Override
public void close() throws Exception {
/** closed flag is updated in the try-catch block. */
try {
reader.close();
closed = true;
System.out.println("Closed BufferedReader in the close method and flag changed to " + closed);
} catch (IOException e) {
System.out.println("Didn't close, and the flag is still " + closed);
throw e;
}
}
}
Full code, including unit tests, can be found here.