21
//: c07:Sandwich.java
// Order of constructor calls.
// package c07;
// import com.bruceeckel.simpletest.*;

import java.util.*;

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 {
//  private static Test monitor = new Test();
  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();
   /*
   monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });
    // */
  }
} ///:~

The output of this code is

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

Since the fields in a class are created in the order they are declared, why don't

Bread()
Cheese()
Lettuce()

come at the top of the above list?

Also, what is it trying to do in this code?

   monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });  

At first I thought it was an anonymous class, but it doesn't look like it. Is it initializing a String array? Why doesn't it have the name for the String variable? Please tell me the name of the programming construct being used here.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
user13267
  • 6,871
  • 28
  • 80
  • 138
  • 3
    But isn't this is your homework? – Mark Jul 23 '13 at 09:31
  • @ChristianMark This definetely ISN'T homework, I had the same question myself while reading Thinking in Java. I find this question and the answers VERY USEFUL :) – Hello Lili Jan 27 '16 at 18:31

4 Answers4

25

The constructor:

public Sandwich() {
    System.out.println("Sandwich()");
}

Is translated by the compiler to:

public Sandwich() {
    super();   // Compiler adds it if it is not explicitly added by programmer
    // All the instance variable initialization is moved here by the compiler.
    b = new Bread();
    c = new Cheese();
    l = new Lettuce();

    System.out.println("Sandwich()");
}

So, the first statement in a constructor is the chaining of super class constructor. In fact, the first statement in any constructor chains to the super class constructor, for that matter. That is why first the super class constructor PortableLunch is invoked, which again chains the call to it's super class constructor, due to the super() added by the compiler (remember?).

This chaining of constructor call is done till the class at the top of the inheritance hierarchy, thereby invoking the Object class constructor at the end.

Now, after each the super class constructor has been executed, and all the super class fields have been initialized, the immediate subclass constructor start the execution after the super() call. And then finally it comes back to the Sandwitch() constructor, which now initializes your 3 fields.

So, basically your fields are initialized at last, and hence they are printed at the end, just before Sandwitch() is printed.

Refer to JLS - §12.5 - Creation of New Class Instance for detailed explanation of the instance creation process.


As for your 2nd part of the question:

monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });  

This code is creating an unnamed array, and initializing it some string literals at the same time. It is similar to the way you would create a named array:

String[] arr = new String[] { "rohit", "jain" };
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Would it have been the same case if they had been primitives instead of objects? For example, if there had been int b = 5 int c =10 and int l = 15, and `new Sandwich();` had not been called, would the compiler still allocate memory for b, c and l? – user13267 Jul 23 '13 at 10:00
  • @user13267. Well, if you don't even call `new Sandwitch()`? Nothing will happen in any case. Instance fields will only get initialized when you create an instance of your class. By doing `new Sandwitch()`, you are actually instantiating your class. – Rohit Jain Jul 23 '13 at 10:16
  • I understand now. I seem to have got C++ and Java mixed up. Thank you very much. – user13267 Jul 23 '13 at 10:36
5

The objects in your example use inheritance, which causes a chain of constructors to be called. When using inheritance a class inheriting from another (subtype) must call the constructor of the class it extends (super type). When a type hierarchy exists, meaning several classes extend from each other in a chain, the calls to the super constructor propagate to the first class in chain that does not inherit from another class (ignoring Object).

The super class constructors of a subtype must be called prior to the execution of the subtype's constructor since it may rely upon fields or methods in the super types. The constructor calls chain up the type hierarchy and once each constructor has initialized the subtype begins its instantiation.

Once the super type constructors in a classes type hierarchy have been called the fields of the subtype are declared since they may also be needed by the subtype's constructor. After the fields of the subtype are declared the subtype constructor executes.

This order is necessary because the subtype may rely upon fields or methods established in the super type.

Meal() (Top of Class Hierarchy, not including Object)
Lunch() (Extends Meal)
PortableLunch() (Extends Lunch)
Bread() (Field of lunch declared before constructor call)
Cheese() (Field of lunch declared before constructor call)
Lettuce() (Field of lunch declared before constructor call)
Sandwich() (Extends Portable Lunch)

Here is a really good overview of object creation in Java.

Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
  • but commenting out `new Sandwich();` in main makes it so that there is no output at all. Why is that? bread, Cheese and Lettuce are independent classes. even though an object of Sandwich is not created, shouldn't the constructors of the other fields be called, since they have been explicitly declared in the Sandwich class? – user13267 Jul 23 '13 at 09:41
  • @user13267 But where are they instantiated? – zEro Jul 23 '13 at 09:42
  • @user13267 Thats because no object is being instantiated so the calls to all of the constructors are never made, causing all of the print statements never to be called. Basically the execution path is never followed that prints out all of the debug messages. – Kevin Bowersox Jul 23 '13 at 09:45
1
new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    }

\the above is anonymous array declaration. You don't need to specify size in this case.

All your super class constructors will be invoked first before initializing your instance variables. i.e,

order--- >

  Object(), 
  all your superclass constructors,
  instance variables of this class in that order
Community
  • 1
  • 1
PermGenError
  • 45,977
  • 8
  • 87
  • 106
1

The constructor is called first, then its instance variables are evaluated, acording to the order specified in the Java Language Specification. That's why you get

Bread()
Cheese()
Lettuce()

after

Meal()
Lunch()
PortableLunch()

about the String[] initialization, as you think is not a anonymous class, is just a String object creation, which doesn't nesesarily have to be assigned to a variable.

morgano
  • 17,210
  • 10
  • 45
  • 56