here is the code
public class ClassResolution {
static class Parent {
public static String name;
static {
System.out.println("this is Parent");
name = "Parent";
}
}
static class Child extends Parent {
static {
System.out.println("this is Child");
name = "Child";
}
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(Child.name);
}}
what ouput i expect is:
this is Parent
this is Child
Child
but actually is:
this is Parent
Parent
it seems static block in Child class does not get executed, but why? it's anti-intuition, doesn't it?
supplement:
to make it more clear, i list the 2 1 points below:
- As @axtavt say, according to JLS 12.4.1, class Child is loaded, but not initialized.
But @Alexei Kaigorodov pointed out, according to jvms-5.5, class Child should be initialized, because of the execution of instruction getstatic on Child class.
what do you think?
supplement2:
@Alexei Kaigorodov has renewed his mind, so it seems no disagreement left. But I think the point of Alexei Kaigorodov is enlightening, so I left it there.
Thank you, everyone.
JLS#12.4.1. When Initialization Occurs
I guess above says it all..
As
Child.name
is reallyParent.name
, Child is not needed.You might find this interesting.
prints
The
javap
for this class prints that the references toChild
are retained.Oops, I was wrong, the reference to Child was not removed and the class was really loaded, but not initialized. Congratulations, you have found a JVM bug. Go to Oracle site and file it. Let me know if you don't want to do so, I'll do it myself.
EDIT: I was wrong again, see comment below. This is not a bug, this is a "gotcha".
From JLS 12.4.1:
As you can see, nothing of these happens in your code (note that
name
is declared inParent
, not inChild
), thereforeChild
doesn't get initialized and its static block doesn't get executed.If you do something to trigger initialization of
Child
, you'll get an expected output:Note, however, that static fields are not inherited, therefore
Child.name
andParent.name
actually refer to the same field. That's why it doesn't make much sense to use code similar to your example in practice.Also note that despite the fact that
Child.name
actually refers toParent.name
, it's still referenced asChild.name
in the bytecode, therefore your code triggers loading ofChild
, but not its initialization.In short,
Child.name
is equal toParent.name
, and the compiler compiles it like so.Because
name
is a static field of the Parent class, it is a class method in Parent, not Child. The Java compiler allows a shortcut where child classes can call static parent class methods/fields as if they come from their own class, but internally they are compiled with regards to the parent class.Although your code refers to
Child.name
, it is internallyParent.name
, as handled by the compiler. Then, because the Child class isn't initialized, the<clinit>
static initializer block is never run, andname
remains "Parent".