views:

684

answers:

3

Say a project contains several classes, each of which has a static initializer block. In what order do those blocks run? I know that within a class, such blocks are run in the order they appear in the code. I've read that it's the same across classes, but some sample code I wrote disagrees with that. I used this code:

package pkg;

public class LoadTest {
    public static void main(String[] args) {
        System.out.println("START");
        new Child();
        System.out.println("END");
    }
}

class Parent extends Grandparent {
    // Instance init block
    {
        System.out.println("instance - parent");
    }

    // Constructor
    public Parent() {
        System.out.println("constructor - parent");
    }

    // Static init block
    static {
        System.out.println("static - parent");
    }
}

class Grandparent {
    // Static init block
    static {
        System.out.println("static - grandparent");
    }

    // Instance init block
    {
        System.out.println("instance - grandparent");
    }

    // Constructor
    public Grandparent() {
        System.out.println("constructor - grandparent");
    }
}

class Child extends Parent {
    // Constructor
    public Child() {
        System.out.println("constructor - child");
    }

    // Static init block
    static {
        System.out.println("static - child");
    }

    // Instance init block
    {
        System.out.println("instance - child");
    }
}

and got this output:

START
static - grandparent
static - parent
static - child
instance - grandparent
constructor - grandparent
instance - parent
constructor - parent
instance - child
constructor - child
END

The obvious answer from that is that parents' blocks run before their children's, but that could just be a coincidence and doesn't help if two classes aren't in the same hierarchy.

EDIT:

I modified my example code by appending this to LoadTest.java:

class IAmAClassThatIsNeverUsed {
    // Constructor
    public IAmAClassThatIsNeverUsed() {
        System.out.println("constructor - IAACTINU");
    }

    // Instance init block
    {
        System.out.println("instance - IAACTINU");
    }

    // Static init block
    static {
        System.out.println("static - IAACTINU");
    }
}

As implied by the class name, I never referenced the new class anywhere. The new program produced the same output as the old one.

+6  A: 

The static initializer for a class gets run when the class is loaded. The class is first loaded when you first access it, either to create an instance, or to access a static method or field.

So, for multiple classes, this totally depends on the code that's run to cause those classes to get loaded.

Chris Jester-Young
A class can get loaded during a dependent's resolve cycle. Simply having a declaration of a variable of a given class will require the loading of that class.
That seems contrary to what section 12.4 (see Keith's excellent answer) says.
Chris Jester-Young
I agree. The variable declaration will (I think) not do it, unless it's a static variable with initialization.
Carl Smotricz
Right, and if you're not initialising with null, then you have to call a constructor or factory method, both of which call cause the class to get loaded.
Chris Jester-Young
"Resolution is the process of checking symbolic references from Test to other classes and interfaces, by loading the other classes and interfaces that are mentioned and checking that the references are correct" seems to indicate its JVM implementation specific.
+17  A: 

See section 12.4 and 12.5 of the Java Language Specification, they go into gory detail about all of this (12.4 for static and 12.5 for instance variables).

Keith Randall
Best possible answer. Great job!
Chip Uni
Good answer. I scanned the page and it contains the fundamentals for object oriented design as it relates to java. So it is a must read for every java developer.
Peter Schuetze
+2  A: 

Keith's and Chris's answers are both great, I'm just adding some more detail for my specific question.

Static init blocks run in the order in which their classes are initialized. So, what order is that? Per JLS 12.4.1:

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • T is a class and a static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.

Invocation of certain reflective methods in class Class and in package java.lang.reflect also causes class or interface initialization. A class or interface will not be initialized under any other circumstance.

To illustrate, here's a walkthrough of what's happening in the example:

  1. Enter main
  2. Print "START"
  3. Attempt to create first instance of Child, which requires initialization of Child
  4. Attempting to initialize Child causes initialization of Parent
  5. Attempting to initialize Parent causes initialization of Grandparent
  6. At the start of initialization of Grandparent, Grandparent's static initialization block is run
  7. Technically, Object gets the last say in the initialization chain by virtue of being Grandparent's parent, but it has nothing to contribute
  8. After Grandparent's static initialization block ends, program falls back to Parent's static initialization block
  9. After Parent's static initialization block ends, program falls back to Child's static initialization block
  10. At this point, Child is initialized, so its constructor may proceed
  11. Since IAmAClassThatIsNeverUsed never gets referenced, none of its code ever runs, including static initializer blocks
  12. The rest of this walkthrough doesn't concern static initializers and is included only for completeness
  13. Child's constructor implicitly calls super() (i.e., Parent's constructor)
  14. Parent's constructor implicitly calls super() (i.e., Grandparent's constructor)
  15. Grandparent's constructor does the same, which has no effect (again, Object has nothing to contribute)
  16. Immediately after Grandparent's constructor's call to super() comes Grandparent's instance initializer block
  17. The rest of Grandparent's constructor's constructor runs and the constructor terminates
  18. The program falls back to Parent's constructor, immediately after its call to super() (i.e., Grandparent's constructor) resolves
  19. As above, Parent's instance initializer does its thing and its constructor finishes up
  20. Similarly, the program returns to and completes Child's constructor
  21. At this point, the object has been instantiated
  22. Print "END"
  23. Terminate normally
Lord Torgamus
in 12.4.1 we also find: `Before a class is initialized, its direct superclass must be initialized, ...`
Carlos Heuberger