Team LiB
Previous Section Next Section

3.3. Creating and Initializing Objects

Now that we've covered fields and methods, we move on to other important members of a class. Constructors and initializers are class members whose job is to initialize the fields of a class.

Take another look at how we've been creating Circle objects:

Circle c = new Circle();

What are those parentheses doing there? They make it look like we're calling a method. In fact, that is exactly what we're doing. Every class in Java has at least one constructor, which is a method that has the same name as the class and whose purpose is to perform any necessary initialization for a new object. Since we didn't explicitly define a constructor for our Circle class in Example 3-1, Java gave us a default constructor that takes no arguments and performs no special initialization.

Here's how a constructor works. The new operator creates a new, but uninitialized, instance of the class. The constructor method is then called, with the new object passed implicitly (a this reference, as we saw earlier) as well as whatever arguments that are specified between parentheses passed explicitly. The constructor can use these arguments to do whatever initialization is necessary.

3.3.1. Defining a Constructor

There is some obvious initialization we could do for our circle objects, so let's define a constructor. Example 3-2 shows a new definition for Circle that contains a constructor that lets us specify the radius of a new Circle object. The constructor also uses the this reference to distinguish between a method parameter and an instance field of the same name.

Example 3-2. A constructor for the Circle class
public class Circle {
    public static final double PI = 3.14159;  // A constant
    public double r;   // An instance field that holds the radius of the circle

    // The constructor method: initialize the radius field
    public Circle(double r) { this.r = r; }   

    // The instance methods: compute values based on the radius
    public double circumference() { return 2 * PI * r; }
    public double area() { return PI * r*r; }
}

When we relied on the default constructor supplied by the compiler, we had to write code like this to initialize the radius explicitly:

Circle c = new Circle();
c.r = 0.25;

With this new constructor, the initialization becomes part of the object creation step:

Circle c = new Circle(0.25);

Here are some important notes about naming, declaring, and writing constructors:

  • The constructor name is always the same as the class name.

  • Unlike all other methods, a constructor is declared without a return type, not even void.

  • The body of a constructor should initialize the this object.

  • A constructor may not return this or any other value. A constructor may include a return statement, but only one that does not include a return value.

3.3.2. Defining Multiple Constructors

Sometimes you want to initialize an object in a number of different ways, depending on what is most convenient in a particular circumstance. For example, we might want to initialize the radius of a circle to a specified value or a reasonable default value. Since our Circle class has only a single instance field, we can't initialize it too many ways, of course. But in more complex classes, it is often convenient to define a variety of constructors. Here's how we can define two constructors for Circle:

public Circle() { r = 1.0; }
public Circle(double r) { this.r = r; }

It is perfectly legal to define multiple constructors for a class, as long as each constructor has a different parameter list. The compiler determines which constructor you wish to use based on the number and type of arguments you supply. This is simply an example of method overloading, as we discussed in Chapter 2.

3.3.3. Invoking One Constructor from Another

A specialized use of the this keyword arises when a class has multiple constructors; it can be used from a constructor to invoke one of the other constructors of the same class. In other words, we can rewrite the two previous Circle constructors as follows:

// This is the basic constructor: initialize the radius
public Circle(double r) { this.r = r; }
// This constructor uses this() to invoke the constructor above
public Circle() { this(1.0); }

The this( ) syntax is a method invocation that calls one of the other constructors of the class. The particular constructor that is invoked is determined by the number and type of arguments, of course. This is a useful technique when a number of constructors share a significant amount of initialization code, as it avoids repetition of that code. This would be a more impressive example, of course, if the one-parameter version of the Circle( ) constructor did more initialization than it does.

There is an important restriction on using this(): it can appear only as the first statement in a constructor. It may, of course, be followed by any additional initialization a particular version of the constructor needs to do. The reason for this restriction involves the automatic invocation of superclass constructor methods, which we'll explore later in this chapter.

3.3.4. Field Defaults and Initializers

Not every field of a class requires initialization. Unlike local variables, which have no default value and cannot be used until explicitly initialized, the fields of a class are automatically initialized to the default value false, '\u0000', 0, 0.0, or null, depending on their type. These default values are guaranteed by Java and apply to both instance fields and class fields.

If the default field value is not appropriate for your field, you can explicitly provide a different initial value. For example:

public static final double PI = 3.14159;
public double r = 1.0;

Field declarations and local variable declarations have similar syntax, but there is an important difference in how their initializer expressions are handled. As described in Chapter 2, a local variable declaration is a statement that appears within a Java method; the variable initialization is performed when the statement is executed. Field declarations, however, are not part of any method, so they cannot be executed as statements are. Instead, the Java compiler generates instance-field initialization code automatically and puts it in the constructor or constructors for the class. The initialization code is inserted into a constructor in the order in which it appears in the source code, which means that a field initializer can use the initial values of any fields declared before it. Consider the following code excerpt, which shows a constructor and two instance fields of a hypothetical class:

public class TestClass {
  public int len = 10;
  public int[] table = new int[len];

  public TestClass() { 
    for(int i = 0; i < len; i++) table[i] = i;
  }

  // The rest of the class is omitted... 
}

In this case, the code generated for the constructor is actually equivalent to the following:

public TestClass() { 
  len = 10;
  table = new int[len];
  for(int i = 0; i < len; i++) table[i] = i;
}

If a constructor begins with a this( ) call to another constructor, the field initialization code does not appear in the first constructor. Instead, the initialization is handled in the constructor invoked by the this( ) call.

So, if instance fields are initialized in constructor methods, where are class fields initialized? These fields are associated with the class, even if no instances of the class are ever created, so they need to be initialized even before a constructor is called. To support this, the Java compiler generates a class initialization method automatically for every class. Class fields are initialized in the body of this method, which is invoked exactly once before the class is first used (often when the class is first loaded by the Java VM.)[2] As with instance field initialization, class field initialization expressions are inserted into the class initialization method in the order in which they appear in the source code. This means that the initialization expression for a class field can use the class fields declared before it. The class initialization method is an internal method that is hidden from Java programmers. In the class file, it bears the name <clinit>.

[2] It is actually possible to write a class initializer for a class C that calls a method of another class that creates an instance of C. In this contrived recursive case, an instance of C is created before the class C is fully initialized. This situation is not common in everyday practice, however.

3.3.4.1 Initializer blocks

So far, we've seen that objects can be initialized through the initialization expressions for their fields and by arbitrary code in their constructor methods. A class has a class initialization method, which is like a constructor, but we cannot explicitly define the body of this method as we can for a constructor. Java does allow us to write arbitrary code for the initialization of class fields, however, with a construct known as a static initializer. A static initializer is simply the keyword static followed by a block of code in curly braces. A static initializer can appear in a class definition anywhere a field or method definition can appear. For example, consider the following code that performs some nontrivial initialization for two class fields:

// We can draw the outline of a circle using trigonometric functions
// Trigonometry is slow, though, so we precompute a bunch of values
public class TrigCircle {
  // Here are our static lookup tables and their own simple initializers
  private static final int NUMPTS = 500;
  private static double sines[] = new double[NUMPTS];
  private static double cosines[] = new double[NUMPTS];

  // Here's a static initializer that fills in the arrays 
  static {
    double x = 0.0;
    double delta_x = (Circle.PI/2)/(NUMPTS-1);
    for(int i = 0, x = 0.0; i < NUMPTS; i++, x += delta_x) {
      sines[i] = Math.sin(x);
      cosines[i] = Math.cos(x);
    }
  }
  // The rest of the class is omitted... 
}

A class can have any number of static initializers. The body of each initializer block is incorporated into the class initialization method, along with any static field initialization expressions. A static initializer is like a class method in that it cannot use the this keyword or any instance fields or instance methods of the class.

In Java 1.1 and later, classes are also allowed to have instance initializers. An instance initializer is like a static initializer, except that it initializes an object, not a class. A class can have any number of instance initializers, and they can appear anywhere a field or method definition can appear. The body of each instance initializer is inserted at the beginning of every constructor for the class, along with any field initialization expressions. An instance initializer looks just like a static initializer, except that it doesn't use the static keyword. In other words, an instance initializer is just a block of arbitrary Java code that appears within curly braces.

Instance initializers can initialize arrays or other fields that require complex initialization. They are sometimes useful because they locate the initialization code right next to the field, instead of separating into a constructor method. For example:

private static final int NUMPTS = 100;
private int[] data = new int[NUMPTS];
{ for(int i = 0; i < NUMPTS; i++) data[i] = i; }

In practice, however, this use of instance initializers is fairly rare. Instance initializers were introduced in Java 1.1 to support anonymous inner classes, which are not allowed to define constructors. (Anonymous inner classes are covered in Section 3.10 later in this chapter.)

    Team LiB
    Previous Section Next Section