...
The SubClass
overrides the protected finalize
method and performs cleanup. Subsequently, it calls super.finalize()
to make sure its superclass is also finalized. The unsuspecting BaseClass
calls the doLogic()
method which happens to be overridden in the SubClass
. This resurrects a reference to SubClass
such that it is not only prevented from getting garbage collected but also cannot use its finalizer anymore in order to close new resources that may have been allocated by the called method. As detailed in MET32-J. Ensure that constructors do not call overridable methods, if the subclass's finalizer has terminated key resources, invoking its methods from the superclass might lead one to observe the object in an inconsistent state and in the worst case result in the infamous NullPointerException
.
Code Block | ||
---|---|---|
| ||
class BaseClass { protected void finalize() throws Throwable { System.out.println("Superclass finalize!"); doLogic(); } public void doLogic() throws Throwable{ System.out.println("This is super-class!"); } } class SubClass extends BaseClass { protected void finalize() throws Throwable { System.out.println("Subclass finalize!"); try { // cleanup resources } finally { super.finalize(); // call BaseClass' finalizer } } public void doLogic() throws Throwable{ System.out.println("This is sub-class!"); // any resource allocations made here will persist } } public class BadUse { public static void main(String[] args) { try { BaseClass bc = new SubClass(); System.runFinalizersOnExit(true); // artificially simulate finalization (do not do this) } catch (Throwable t) { /* handle error */ } } } |
...