Constructors' order of execution regarding variables' initialization

Inside the class, variables' initialization order is determined by their positions in codes. Even the variables are defined all over the place among method definitions, they are still initialized before any methods (including the constructors) are called.
See codes below:


        class Tag {
            Tag(int marker) {
                private int t;
                System.out.println("Tag(" + marker + ")");
            }
        }
        class Card {
            Tag t1 = new Tag(1); // Before constructor
            Card() {
                //method definition
                // Indicate we're in the constructor:
                System.out.println("Card()");
                t3 = new Tag(33); // Reinitialize t3
            }
            Tag t2 = new Tag(2); // After constructor
            void f() {
                System.out.println("f()");
            }
            Tag t3 = new Tag(3); // At end
        }
        public class OrderOfInitialization {
            public static void main(String[] args) {
                Card t = new Card();
                t.f(); // Shows that construction is done
            }
        }
        

Based on the variables are initialized before methods' initialization principle, when main class: OrderOfInitialization was called, at Card t = new Card();, compiler will execute Tag t1 = new Tag(1);, Tag t2 = new Tag(2); and Tag t3 = new Tag(3);, the positions where these variables' declarations reside are irrelevant. After all the variables are initialized, methods begin to initialize. e.g. Card() {……}, then finally at t.f();, void f() {……} will be called.

Initialization of static variables: first statics, then non-statics

See codes below:


        package gy;

        class Bowl {
            Bowl(int marker) {
                System.out.println("Bowl(" + marker + ")");
        }
        void f(int marker) {
                System.out.println("f(" + marker + ")");
            }
        }

        class Table {
            static Bowl b1 = new Bowl(1);
            Table() {
                System.out.println("Table()");
                b2.f(1);
            }
            void f2(int marker) {
                System.out.println("f2(" + marker + ")");
            }
            static Bowl b2 = new Bowl(2);
        }

        class Cupboard {
            Bowl b3 = new Bowl(3);
            static Bowl b4 = new Bowl(4);
        Cupboard() {
                System.out.println("Cupboard()");
                b4.f(2);
            }
            void f3(int marker) {
                System.out.println("f3(" + marker + ")");
            }
            static Bowl b5 = new Bowl(5);
        }

        public class StaticInitialization {
            public static void main(String[] args) {
                System.out.println("Creating new Cupboard() in main");
                new Cupboard();
                System.out.println("Creating new Cupboard() in main");
                new Cupboard();
                t2.f2(1);
                t3.f3(1);
            }
            static Table t2 = new Table();
            static Cupboard t3 = new Cupboard();
        }
        

Because variables are initialized before methods' initialization, when main class: StaticInitialization is called, static Table t2 = new Table(); and static Cupboard t3 = new Cupboard(); are first executed. Then is public static void main(String[] args) {……}. One thing to notice is even though static variables are initialized before non-static variables, they are only going to be initialized once. So after static Table t2 = new Table(); and static Cupboard t3 = new Cupboard(); were executed, next time when class Cupboard's constructor is called, static Bowl b4 = new Bowl(4); as well as static Bowl b5 = new Bowl(5); would not be executed again. That's why in result below there are duplicated 'Bowl(4)', 'Bowl(5)' outputs because they are already initialized.

Program Output:


            Bowl(1)
            Bowl(2)
            Table()
            f(1)
            Bowl(4)
            Bowl(5)
            Bowl(3)
            Cupboard()
            f(2)
            Creating new Cupboard() in main
            Bowl(3)
            Cupboard()
            f(2)
            Creating new Cupboard() in main
            Bowl(3)
            Cupboard()
            f(2)
            f2(1)
            f3(1)

            my presumption:
            
            Bowl(1) √
            Bowl(2) √
            Table() √
            f(1) √
            Bowl(4) √
            Bowl(5) √
            Bowl(3) √
            Cupboard() √
            f(2) √
            Creating new Cupboard() in main √
            Bowl(4)×
            Bowl(5) ×
            Bowl(3) √
            Cupboard()√
            f(2) √
            Creating new Cupboard() in main √
            Bowl(4) ×
            Bowl(5) ×
            Bowl(3) √
            Cupboard()√
            f(2) √
            f2(1) √
            f3(1) √
        

Call order for constructors

In an inheritance hierarchy, compiler sees a calling for super() method in every class's definition. Note this super() calls super constructor, which will call its super and so on till Object's constructor.


        package gy;

        class Meal {
            Meal() { System.out.println("Meal()"); }
        }

        class Bread {
            Bread() { System.out.println("Bread()"); }
        }

        class Cheese {
            Cheese() { System.out.println("Cheese()"); }
        }

        class Lettuce {
            Lettuce() { System.out.println("Lettuce()"); }
        }

        class Lunch extends Meal {
            Lunch() { System.out.println("Lunch()"); }
        }

        class PortableLunch extends Lunch {
            PortableLunch() { System.out.println("PortableLunch()");}
        }

        public class Sandwich extends PortableLunch {//public class 后面的类为主类,继承自PortableLunch类
            private Bread b = new Bread();
            private Cheese c = new Cheese();
            private Lettuce l = new Lettuce();
            public Sandwich() {
                System.out.println("Sandwich()");
            }
            public static void main(String[] args) {
                new Sandwich(); 
            }
        }
        

Output is expected:


            Meal()
            Lunch()
            PortableLunch()
            Bread()
            Cheese()
            Lettuce()
            Sandwich()
        

Through IDE's debug mode I find that in main class, variables aren't exactly initialized before methods' initialization.
For instance at private Bread b = new Bread(); the public static void main(String[] args) {……}is first called, that's method's initialization before variables.
The reason is that during compilation the rule "static variables are initialized before non-static variables" has priority than rule "variables are initialized before methods' initialization".
To prove that, simply change private Bread b = new Bread(); to private static Bread b = new Bread(); than compiler would start from private static Bread b = new Bread();.
When public static void main(String[] args) {……} is called, compiler find this main class is in inheritance hierarchy, so Classloader will add PortableLunch, then only to find PortableLunch is in inheritance hierarchy and so on.
Finally, it reaches "base class": Meal and adds that, output Meal(), then to Meal's child Lunch, output Lunch(), then to PortableLunch, output PortableLunch(), now static parts are done initializing.
Now variables' initialization starts. Outputing Bread(), Cheese(), Lettuce().
At last methods' initialization kicks in: public Sandwich() {…}, outputting Sandwich()

Before all those things I wrote above happens, object's storege are initialized to a binary zero.

See codes below:


        abstract class Glyph {
            abstract void draw();
            Glyph() {
                System.out.println("Glyph() before draw()");
                draw();
                System.out.println("Glyph() after draw()");
            }
        }

        class RoundGlyph extends Glyph {
            RoundGlyph(int r) {
                radius = r;
                System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
            }
            void draw() {
                System.out.println("RoundGlyph.draw(), radius = " + radius);
            }
        }

        public class PolyConstructors {
            public static void main(String[] args) {
                new RoundGlyph(5);
            }
        }
        

Program Output:


            Glyph() before draw()
            RoundGlyph.draw(), radius = 0
            Glyph() after draw()
            RoundGlyph.RoundGlyph(), radius = 5
        

One can see radius is 0 at the very first, then argument was passed, it became 5.

Order of execution:

public static void main(String[] args) {……}
-> RoundGlyph(int r) {……}
-> Glyph() {System.out.println("Glyph() before draw()");
-> draw();
-> void draw() {……} System.out.println("Glyph() after draw()");
-> private int radius = 1;
-> RoundGlyph(int r) {……}

One can see variables aren't exactly initialized before methods' initialization here as well.

At public static void main(String[] args) {……}, private int radius = 1; did not get executed first. Instead, compiler went to constructor RoundGlyph(int r) {……}.
That is because compiler only went to RoundGlyph(int r) {……} but did not execute. Because RoundGlyph is child to Glyph so after Classloader add RoundGlyph(int r) {……} it goes straight to its super: Glyph and executed.
If there isn't inheritance hierarchy in the first place, after going to RoundGlyph(int r) {……} but did not execute, compiler would go to private int radius = 1; and initialize the variable.

So compiler would first go to constructor but did not execute. Then rule "variables are initialized before methods' initialization" takes effect.