The Artima Developer Community
Sponsored Link

Object Initialization in Java
Object Initialization in the Java Language and Virtual Machine
by Bill Venners
First Published in JavaWorld, February 1998

<<  Page 6 of 8  >>

Advertisement

Initialization and inheritance
When an object is initialized, all the instance variables defined in the object's class must be set to proper initial values. While this is necessary, often it is not enough to yield a fully initialized class. An object incorporates not only the fields explicitly declared in its class, but also those declared in its superclasses. To fully initialize an object, therefore, all instance variables declared in its class and in all its superclasses must be initialized.

Instance data of objects
Every object, except class Object itself, has at least one superclass. When an object is created, the Java virtual machine allocates enough space for all the object's instance variables, which include all fields defined in the object's class and in all its superclasses. For example, consider the following classes:

// Declared in file Object.java (not In source packet)
package java.lang;
public class Object {
    // Has no fields
    // Has several methods, not shown...
}

// In source packet in file init/ex14/Liquid.java
class Liquid {
    // Has two fields:
    private int mlVolume;
    private float temperature; // in Celsius
    // Has several methods, not shown...
}

// In source packet in file init/ex14/Coffee.java
class Coffee extends Liquid {
    // Has two fields:
    private boolean swirling;
    private boolean clockwise;
    // Has several methods, not shown...
}


Figure 1. Class Coffee's superclasses and fields

You can see the inheritance hierarchy for class Coffee, as defined above, in Figure 1. This figure, as well as the code above, shows Object as having no instance variables. But it is possible that Object could have instance variables. The actual internal make-up of class Object is a detail specific to each Java platform implementation. It is extremely likely, however, that Object will have no fields in any given Java platform implementation. Because Object is the superclass of all other objects, any fields declared in Object must be allocated for every object used by every Java program.

In Figure 2, you can see the data that must be allocated on the heap for a Coffee object. The part of the heap that is occupied by the instance data for the Coffee object is shown in the cyan color. Keep in mind that the actual manner of representing objects on the heap is an implementation detail of each particular Java virtual machine. This figure represents just one of many possible schemes for storing objects on the heap inside the JVM.


Figure 2. Instance data for a Coffee object

Figure 2 shows that the instance data for a Coffee object includes each instance variable defined in class Coffee and each of Coffee's superclasses. Both of Liquid's fields, mlVolume and temperature, are part of the Coffee object's data, as well as Coffee's fields: swirling and clockwise. This is true even though Coffee doesn't actually inherit the mlVolume and temperature fields from class Liquid.

A note on the word "inherit"
In Java jargon, the word "inherit" has a restricted meaning. A subclass inherits only accessible members of its superclasses -- and only if the subclass doesn't override or hide those accessible members. A class's members are the fields and methods actually declared in the class, plus any fields and methods it inherits from superclasses. In this case, because Liquid's mlVolume and temperature fields are private, they are not accessible to class Coffee. Coffee does not inherit those fields. As a result, the methods declared in class Coffee can't directly access those fields. Despite this, those fields are still part of the instance data of a Coffee object.

Pointers to class data
Figure 2 also shows, as part of the instance data of the Coffee object, a mysterious 4-byte quantity labeled "native pointer to class information." Every Java virtual machine must have the capability to determine information about its class, given only a reference to an object. This is needed for many reasons, including type-safe casting and the instanceof operator.

Figure 2 illustrates one way in which a Java virtual machine implementation could associate class information with the instance data for an object. In this figure, a native pointer to a data structure containing class information is stored along with the instance variables for an object. The details in which the various ways a JVM could connect an object's data with its class information are beyond the scope of this article. The important thing to understand here is that class information will in some way be associated with the instance data of objects, and that the instance data includes fields for an object's class and all its superclasses.

Initializing fields in superclasses
Each class contains code to initialize the fields explicitly declared in that class. Unlike methods, constructors are never inherited. If you don't explicitly declare a constructor in a class, that class will not inherit a constructor from its direct superclass. Instead, the compiler will generate a default constructor for that class. This is because a superclass constructor can't initialize fields in the subclass. A subclass must have its own constructor to initialize its own instance variables. In the class file, this translates to: every class has at least one <init> method responsible for initializing the class variables explicitly declared in that class.

For every object, you can trace a path of classes on an inheritance hierarchy between the object's class and class Object. For the Coffee object described above and shown in Figures 1 and 2, the path is: Coffee, Liquid, Object. To fully initialize an object, the Java virtual machine must invoke (at least) one instance initialization method from each class along the object's inheritance path. In the case of Coffee, this means that at least one instance initialization method must be invoked for each of the classes Coffee, Liquid, and Object.

During initialization, an <init> method may use one field in calculating another field's initial value. While this is perfectly reasonable, it brings up the possibility that a field could be used before it has been initialized to its proper (not default) initial value. As mentioned earlier in this article, Java includes mechanisms that help prevent an instance variable from being used before it has been properly initialized. One mechanism is the rule, enforced by the Java compiler, forbidding initializers from directly using instance variables declared textually after the variable being initialized. Another mechanism is the order in which the fields from each class along an object's inheritance path are initialized: the "order of initialization."

Order of initialization
In Java, the fields of an object are initialized starting with the fields declared in the base class and ending with the fields declared in the object's class. For a CoffeeCup object with the inheritance path shown in Figure 1, the order of initialization of fields would be:

  1. Object's fields (this will be quick, because there are none)
  2. Liquid's fields (mlVolume and temperature)
  3. Coffee's fields (swirling and clockwise)

This base-class-first order aims to prevent fields from being used before they are initialized to their proper (not default) values. In a constructor or initializer, you can safely use a superclass's field directly, or call a method that uses a superclass's field. By the time the code in your constructor or initializer is executed, you can be certain that the fields declared in any superclasses have already been properly initialized.

For example, you could safely use the temperature variable declared in class Liquid when you are initializing the swirling variable declared in class Coffee. (Perhaps if the temperature is above the boiling point for coffee, you set swirling to false.) If temperature were not private, class Coffee would inherit the field, and you could use it directly in an initializer or constructor of class Coffee. In this case, temperature is private, so you'll have to use the temperature field indirectly, through a method:

// In source packet in file init/ex15/Liquid.java
class Liquid {

    private int mlVolume;
    private float temperature; // in Celsius

    public Liquid() {
        mlVolume = 300;
        temperature = (float) (Math.random() * 100.0);
    }

    public float getTemperature() {
        return temperature;
    }
    // Has several other methods, not shown...
}

// In source packet in file init/ex15/Coffee.java
class Coffee extends Liquid {

    private static final float BOILING_POINT = 100.0f; // Celsius
    private boolean swirling;
    private boolean clockwise;

    public Coffee(boolean swirling, boolean clockwise) {
        if (getTemperature() >= BOILING_POINT) {
            // Leave swirling at default value: false
            return;
        }
        this.swirling = swirling;
        if (swirling) {
            this.clockwise = clockwise;
        } // else, leave clockwise at default value: false
    }
    // Has several methods, not shown,
    // but doesn't override getTemperature()...
}

In the example, the constructor for Coffee invokes getTemperature() and uses the return value in the calculation of the proper initial value of swirling and clockwise. getTemperature() returns the value of the temperature variable; thus, the constructor for Coffee uses a field declared in Liquid. This works because, by the time the code inside Coffee's constructor is executed, the instance variables declared in Liquid are guaranteed to have already been initialized to their proper starting values.

<<  Page 6 of 8  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2009 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us