@JonSkeet said everything in technical terms, and I canβt say more. Let me try to explain with an analogy:
Think of static initialization opening the door of a room and executing a constructor, like doing / decorating things in this room.
Now, to open the door of room D, you need to open the door of C, for C you need B, and for B you need A. Now you are in room A, and you have finished doors that open the formalities in room A. And at the end formal door openings in room A you see a note that completes the operation of room C ( A c=new C(); ). Now, since room C and its dependent rooms are already open, you do not need to open it again (which means not initializing a static block). But before you enter Room C, you will end the room. Opening System.out.println("Static A"); , that is, System.out.println("Static A"); So, on the console, you have:
Static A
Now you are in room C, and you need to finish this room, but before that you finish B and A due to dependency (C extends B and B continues A). So in the console you have:
Constr A Constr B Constr C
Now you will return to room A again and see that the formalities for opening the door are completed. So, you will get into room B, then C, and then D. So, in the console you:
Static B Static C Static D
Now you finish the work of room D ( A a=new D(); ), and for this you need to finish the work of C, B and C because of the dependency (D extends C, C continues B and B continues A). So in the console you have:
Constr A Constr B Constr C Constr D