class DemoClass {
public static void main(String args[]) {
System.out.println("Start");
A a=new D();
}
}
class A {
static {
System.out.println("Static A");
A c=new C();
}
public A() {
System.out.println("Constr A");
}
}
class B extends A {
static {
System.out.println("Static B");
}
public B() {
System.out.println("Constr B");
}
}
class C extends B {
static {
System.out.println("Static C");
}
public C() {
System.out.println("Constr C");
}
}
class D extends C {
static {
System.out.println("Static D");
}
public D() {
System.out.println("Constr D");
}
}
The order of execution for above code is:
Start
Static A
Constr A
Constr B
Constr C
Static B
Static C
Static D
Constr A
Constr B
Constr C
Constr D
In my view all the static blocks should be executed first then only the object will be created. But, here the object "A c=new C()" in class A static block is created first and then the other static blocks are executed. Why?
The static initializer of all the classes have started executing - but in order to initialize D, C must be initialized, so B must be initialized, so A must be initialized. At the point where the code in the static initializer in A
is being executed, all of the classes involved are in a state of "being initialized".
Within the static initializer of A
, it constructs an instance of C
- but C
is already being initialized, so the initializer doesn't start again... the JVM just notes that it's already being initialized (within the same thread) and continues.
The details of all of this are in JLS 12.4.2. In particular, the bullet:
If the
Class
object forC
indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. ReleaseLC
and complete normally.
and
Next, if C is a class rather than an interface, and its superclass has not yet been initialized, then let SC be its superclass and let SI1, ..., SIn be all superinterfaces of C that declare at least one default method. [...]
For each S in the list [ SC, SI1, ..., SIn ], recursively perform this entire procedure for S. If necessary, verify and prepare S first.
... are relevant.
@JonSkeet has said everything in technical terms and I cannot say more than that. Let me try to explain using an analogy:
Think of static initialization as opening a door of a room and constructor execution as doing/finishing things in that room.
Now, to open door of room D you need to open door of C, for C you need B and for B you need A. Now, you are in room A and you have finish door opening formalities in room A. And while finishing door opening formalities in room A, you see a note that finish work of room C (A c=new C();
). Now since room C and its dependent rooms are already open you need not to open then again (means no static block initialization). But before you go to room C you will finish the room A opening formalities i.e. System.out.println("Static A");
So, in console you have:
Static A
Now, you are in room C and you have to finish this room but before that you have finish B and A because of dependency (C extends B and B extends A). So, in console you have:
Constr A Constr B Constr C
Now, you will again go back to room A and see door opening formalities are finished. So, you will come to room B then C and then D. So, in console you have:
Static B Static C Static D
Now, you are finishing job of room D (A a=new D();
) and to do that you need to finish job of C, B and C because of dependency (D extends C, C extends B and B extends A). So, in console you have:
Constr A Constr B Constr C Constr D
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With