Objects in Java are allocated on a system “heap” memory
space. Unlike other languages, however, we needn’t manage that memory
ourselves. Java takes care of memory allocation and deallocation for you.
Java explicitly allocates storage for an object when you create it with
the new operator. More
importantly, objects are removed by garbage collection when they’re no
longer referenced.
Objects are allocated with the new operator using an object
constructor. A constructor is a special method with the same
name as its class and no return type. It’s called when a new class
instance is created, which gives the class an opportunity to set up the
object for use. Constructors, like other methods, can accept arguments
and can be overloaded (they are not, however, inherited like other
methods; we’ll discuss inheritance in Chapter 6).
classDate{longtime;Date(){time=currentTime();}Date(Stringdate){time=parseDate(date);}...}
In this example, the class Date
has two constructors. The first takes no arguments; it’s known as the
default constructor. Default constructors play a
special role: if we don’t define any constructors for a class, an empty
default constructor is supplied for us. The default constructor is what
gets called whenever you create an object by calling its constructor
with no arguments. Here we have implemented the default constructor so
that it sets the instance variable time by calling a hypothetical method,
currentTime(), which resembles the
functionality of the real java.util.Date class. The second constructor
takes a String argument. Presumably,
this String contains a string
representation of the time that can be parsed to set the time variable. Given the constructors in the
previous example, we create a Date
object in the following ways:
Datenow=newDate();Datechristmas=newDate("Dec 25, 2006");
In each case, Java chooses the appropriate constructor at compile time based on the rules for overloaded method selection.
If we later remove all references to an allocated object, it’ll be garbage-collected, as we’ll discuss shortly:
christmas=null;// fair game for the garbage collector
Setting this reference to null
means it’s no longer pointing to the "Dec 25,
2006" string object. Setting the variable christmas to any other value would have the
same effect. Unless the original string object is referenced by another
variable, it’s now inaccessible and can be garbage-collected. We’re not
suggesting that you have to set references to null to get the values garbage-collected.
Often this just happens naturally when local variables fall out of
scope, but items referenced by instance variables of objects live as
long as the object itself lives (through references to it) and static
variables live effectively forever.
A few more notes: constructors can’t be declared abstract, synchronized, or final (we’ll define the rest of those terms
later). Constructors can, however, be declared with the visibility
modifiers public, private, or protected, just like
other methods, to control their accessibility. We’ll talk in detail
about visibility modifiers in the next chapter.
A constructor can refer to another constructor in the same
class or the immediate superclass using special forms of the this and super references. We’ll discuss the first case
here and return to that of the superclass constructor after we have
talked more about subclassing and inheritance. A constructor can invoke
another overloaded constructor in its class using the self-referential
method call this() with appropriate
arguments to select the desired constructor. If a constructor calls
another constructor, it must do so as its first
statement:
classCar{Stringmodel;intdoors;Car(Stringmodel,intdoors){this.model=model;this.doors=doors;// other, complicated setup...}Car(Stringmodel){this(model,4/* doors */);}...}
In this example, the class Car
has two constructors. The first, more explicit, one accepts arguments
specifying the car’s model and its number of doors. The second
constructor takes just the model as an argument and, in turn, calls the
first constructor with a default value of four doors. The advantage of
this approach is that you can have a single constructor do all the
complicated setup work; other auxiliary constructors simply feed the
appropriate arguments to that constructor.
The special call to this() must
appear as the first statement in our delegating constructor. The syntax
is restricted in this way because there’s a need to identify a clear
chain of command in the calling of constructors. At the end of the
chain, Java invokes the constructor of the superclass (if we don’t do it
explicitly) to ensure that inherited members are initialized properly
before we proceed.
There’s also a point in the chain, just after invoking the constructor of the superclass, where the initializers of the current class’s instance variables are evaluated. Before that point, we can’t even reference the instance variables of our class. We’ll explain this situation again in complete detail after we have talked about inheritance.
For now, all you need to know is that you can invoke a second constructor (delegate to it) only as the first statement of your constructor. For example, the following code is illegal and causes a compile-time error:
Car(Stringm){intdoors=determineDoors();this(m,doors);// Error: constructor call// must be first statement}
The simple model name constructor can’t do any additional setup before calling the more explicit constructor. It can’t even refer to an instance member for a constant value:
classCar{...finalintdefault_doors=4;...Car(Stringm){this(m,default_doors);// Error: referencing// uninitialized variable}...}
The instance variable defaultDoors is not initialized until a later
point in the chain of constructor calls setting up the object, so the
compiler doesn’t let us access it yet. Fortunately, we can solve this
particular problem by using a static variable instead of an instance
variable:
classCar{...staticfinalintDEFAULT_DOORS=4;...Car(Stringm){this(m,DEFAULT_DOORS);// Okay!}...}
The static members of a class are initialized when the class is first loaded into the virtual machine, so it’s safe to access them in a constructor.
It’s possible to declare a block of code (some statements
within curly braces) directly within the scope of a class. This code
block doesn’t belong to any method; instead, it’s executed once, at the
time the object is constructed, or, in the case of a code block marked
static, at the time the
class is loaded. These blocks can be used to do additional setup for the
class or an object instance and are called initializer
blocks.
Instance initializer blocks can be thought of as extensions of instance variable initialization. They’re called at the time the instance variable’s initializers are evaluated (after superclass construction, but before your constructor body), in the order in which they appear in the Java source:
classMyClass{PropertiesmyProps=newProperties();// set up myProps{myProps.put("foo","bar");myProps.put("boo","gee");}inta=5;...
Normally, this kind of setup could be done just as well in the object’s constructor. A notable exception is in the case of an anonymous inner class (see Chapter 6).
Similarly, you can use static initializer blocks to set up static class members. This more useful case allows the static members of a class to have complex initialization just like objects do with constructors:
classColorWheel{staticHashtablecolors=newHashtable();// set up colorsstatic{colors.put("Red",Color.red);colors.put("Green",Color.green);colors.put("Blue",Color.blue);...}...}
The class ColorWheel provides a
variable, colors, that maps the names
of colors to Color objects in a
Hashtable. The first time the class
ColorWheel is referenced and loaded,
the static components of ColorWheel
are evaluated in the order they appear in the source. In this case, the
static code block simply adds elements to the colors table.