Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions

  




 

 

Thinking in Java
Prev Contents / Index Next

Constructor initialization

The constructor can be used to perform initialization, and this gives you greater flexibility in your programming because you can call methods and perform actions at run time to determine the initial values. There’s one thing to keep in mind, however: You aren’t precluding the automatic initialization, which happens before the constructor is entered. So, for example, if you say:

class Counter {
  int i;
  Counter() { i = 7; }
  // . . .


then i will first be initialized to 0, then to 7. This is true with all the primitive types and with object references, including those that are given explicit initialization at the point of definition. For this reason, the compiler doesn’t try to force you to initialize elements in the constructor at any particular place, or before they are used—initialization is already guaranteed.[24]

Order of initialization

Within a class, the order of initialization is determined by the order that the variables are defined within the class. The variable definitions may be scattered throughout and in between method definitions, but the variables are initialized before any methods can be called—even the constructor. For example:

//: c04:OrderOfInitialization.java
// Demonstrates initialization order.
import com.bruceeckel.simpletest.*;

// When the constructor is called to create a
// Tag object, you'll see a message:
class Tag {
  Tag(int marker) {
    System.out.println("Tag(" + marker + ")");
  }
}

class Card {
  Tag t1 = new Tag(1); // Before constructor
  Card() {
    // 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 {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Card t = new Card();
    t.f(); // Shows that construction is done
    monitor.expect(new String[] {
      "Tag(1)",
      "Tag(2)",
      "Tag(3)",
      "Card()",
      "Tag(33)",
      "f()"
    });
  }
} ///:~


In Card, the definitions of the Tag objects are intentionally scattered about to prove that they’ll all get initialized before the constructor is entered or anything else can happen. In addition, t3 is reinitialized inside the constructor.

From the output, you can see that, the t3 reference gets initialized twice: once before and once during the constructor call. (The first object is dropped, so it can be garbage collected later.) This might not seem efficient at first, but it guarantees proper initialization—what would happen if an overloaded constructor were defined that did not initialize t3 and there wasn’t a “default” initialization for t3 in its definition?

Static data initialization

When the data is static, the same thing happens; if it’s a primitive and you don’t initialize it, it gets the standard primitive initial values. If it’s a reference to an object, it’s null unless you create a new object and attach your reference to it.

If you want to place initialization at the point of definition, it looks the same as for non-statics. There’s only a single piece of storage for a static, regardless of how many objects are created. But the question arises of when the static storage gets initialized. An example makes this question clear:

//: c04:StaticInitialization.java
// Specifying initial values in a class definition.
import com.bruceeckel.simpletest.*;

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 {
  static Test monitor = new Test();
  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);
    monitor.expect(new String[] {
      "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)"
    });
  }
  static Table t2 = new Table();
  static Cupboard t3 = new Cupboard();
} ///:~


Bowl allows you to view the creation of a class, and Table and Cupboard create static members of Bowl scattered through their class definitions. Note that Cupboard creates a non-static Bowl b3 prior to the static definitions.

From the output, you can see that the static initialization occurs only if it’s necessary. If you don’t create a Table object and you never refer to Table.b1 or Table.b2, the static Bowl b1 and b2 will never be created. They are initialized only when the first Table object is created (or the first static access occurs). After that, the static objects are not reinitialized.

The order of initialization is statics first, if they haven’t already been initialized by a previous object creation, and then the non-static objects. You can see the evidence of this in the output.

It’s helpful to summarize the process of creating an object. Consider a class called Dog:

  1. The first time an object of type Dog is created (the constructor is actually a static method), or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it does by searching through the classpath.
  2. As Dog.class is loaded (creating a Class object, which you’ll learn about later), all of its static initializers are run. Thus, static initialization takes place only once, as the Class object is loaded for the first time.
  3. When you create a new Dog( ), the construction process for a Dog object first allocates enough storage for a Dog object on the heap.
  4. This storage is wiped to zero, automatically setting all the primitives in that Dog object to their default values (zero for numbers and the equivalent for boolean and char) and the references to null.
  5. Any initializations that occur at the point of field definition are executed.
  6. Constructors are executed. As you shall see in Chapter 6, this might actually involve a fair amount of activity, especially when inheritance is involved. Explicit static initialization

    Java allows you to group other static initializations inside a special “static clause” (sometimes called a static block) in a class. It looks like this:

    class Spoon {
      static int i;
      static {
        i = 47;
      }
      // . . .


    It appears to be a method, but it’s just the static keyword followed by a block of code. This code, like other static initializations, is executed only once: the first time you make an object of that class or the first time you access a static member of that class (even if you never make an object of that class). For example:

    //: c04:ExplicitStatic.java
    // Explicit static initialization with the "static" clause.
    import com.bruceeckel.simpletest.*;
    
    class Cup {
      Cup(int marker) {
        System.out.println("Cup(" + marker + ")");
      }
      void f(int marker) {
        System.out.println("f(" + marker + ")");
      }
    }
    
    class Cups {
      static Cup c1;
      static Cup c2;
      static {
        c1 = new Cup(1);
        c2 = new Cup(2);
      }
      Cups() {
        System.out.println("Cups()");
      }
    }
    
    public class ExplicitStatic {
      static Test monitor = new Test();
      public static void main(String[] args) {
        System.out.println("Inside main()");
        Cups.c1.f(99);  // (1)
        monitor.expect(new String[] {
          "Inside main()",
          "Cup(1)",
          "Cup(2)",
          "f(99)"
        });
      }
      // static Cups x = new Cups();  // (2)
      // static Cups y = new Cups();  // (2)
    } ///:~


    The static initializers for Cups run when either the access of the static object c1 occurs on the line marked (1), or if line (1) is commented out and the lines marked (2) are uncommented. If both (1) and (2) are commented out, the static initialization for Cups never occurs. Also, it doesn’t matter if one or both of the lines marked (2) are uncommented; the static initialization only occurs once.

    Non-static instance initialization

    Java provides a similar syntax for initializing non-static variables for each object. Here’s an example:

    //: c04:Mugs.java
    // Java "Instance Initialization."
    import com.bruceeckel.simpletest.*;
    
    class Mug {
      Mug(int marker) {
        System.out.println("Mug(" + marker + ")");
      }
      void f(int marker) {
        System.out.println("f(" + marker + ")");
      }
    }
    
    public class Mugs {
      static Test monitor = new Test();
      Mug c1;
      Mug c2;
      {
        c1 = new Mug(1);
        c2 = new Mug(2);
        System.out.println("c1 & c2 initialized");
      }
      Mugs() {
        System.out.println("Mugs()");
      }
      public static void main(String[] args) {
        System.out.println("Inside main()");
        Mugs x = new Mugs();
        monitor.expect(new String[] {
          "Inside main()",
          "Mug(1)",
          "Mug(2)",
          "c1 & c2 initialized",
          "Mugs()"
        });
      }
    } ///:~


    You can see that the instance initialization clause:

      {
        c1 = new Mug(1);
        c2 = new Mug(2);
        System.out.println("c1 & c2 initialized");
      }


    looks exactly like the static initialization clause except for the missing static keyword. This syntax is necessary to support the initialization of anonymous inner classes (see Chapter 8).
    Thinking in Java
    Prev Contents / Index Next


 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire