This works as expected, and I don't think the finalize()
method is treated any differently to any other method in Java. What could be considered a bit different is that the finalize()
method is normally only called by the JVM Garbage Collector itself, as outlined in the JavaDoc:
Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.
Also note that Josh Bloch strongly cautions against the use of finalizers in Effective Java:
Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behaviour, poor performance, and portability problems. Finalizers have a few valid uses ... but as a rule of thumb you should avoid finalizers.
Consider the following example, which is similar to yours:
A baseclass with an overriden finalize()
method.
public abstract class BaseClass {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("BaseClass finalisation occured");
}
}
A sub-class which does not override finalize:
public class SubClass extends BaseClass {
public void foo() {
System.out.println("SubClass Foo'd");
}
}
And a driver class with a basic main method to run everything:
public class Driver {
public static void main(String[] args) {
SubClass sc = new SubClass();
sc.foo();
sc = null;
System.gc();
}
}
The output we get is the following:
SubClass Foo'd
BaseClass finalisation occured
What happens with Java method lookup (in very simple terms) is that any method is looked for in the current class, and if not the class hierarchy is climbed until the required method is found. In the above example, when the foo()
method is called on a SubClass
object, the SubClass
class contains the method definition so that implementation is used, and the class hierarchy is not climbed any higher. When the finalize()
method is called (because a System.gc()
has been requested), the method will first be looked for in the SubClass
class, but since that does not contain an implementation of finalize()
its parent (BaseClass
) is searched. BaseClass
does contain an implementation of finalize()
so that is used, and a line is printed to stdout.
Now consider a sub-sub-class which overrides finalize()
again:
public class OverridenSubClass extends SubClass {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Overriden finalize called, which calls super's finalize first");
}
}
And a slightly modified Driver
class:
public class Driver {
public static void main(String[] args) {
OverridenSubClass sc = new OverridenSubClass();
sc.foo();
System.out.println(sc.toString());
sc = null;
System.gc();
System.exit(0);
}
}
Which produces the following output:
SubClass Foo'd
finalize.OverridenSubClass@7150bd4d
BaseClass finalisation occured
Overriden finalize called, which calls initial finalize first
Hopefully this is as expected. The only interesting things to note here are that:
- We don't override
toString()
in any of our classes so the Object.toString()
implementation is used.
- The type of variable
sc
is not what determines the method implementation used - it is the type of the actual object referenced by sc