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 {When we relied on the default constructor supplied by the compiler,
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; }
}
we had to write code like this to initialize the radius explicitly:
Circle c = new Circle();With this new constructor, the initialization becomes part of the
c.r = 0.25;
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; }It is perfectly
public Circle(double r) { this.r = r; }
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 radiusThe this(
public Circle(double r) { this.r = r; }
// This constructor uses this() to invoke the constructor above
public Circle() { this(1.0); }
) 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;Field declarations and local variable declarations have similar
public double r = 1.0;
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 {In this case, the code generated for the constructor is actually
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...
}
equivalent to the following:
public TestClass() {If a constructor begins with a
len = 10;
table = new int[len];
for(int i = 0; i < len; i++) table[i] = i;
}
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 functionsA class can have any number of static initializers. The body of each
// 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...
}
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;In practice, however, this use of instance initializers is fairly
private int[] data = new int[NUMPTS];
{ for(int i = 0; i < NUMPTS; i++) data[i] = i; }
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.)