Object Creation

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.

Constructors

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).

    class Date {
        long time;

        Date() {
            time = currentTime();
        }

        Date( String date ) {
            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:

    Date now = new Date();
    Date christmas = new Date("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.

Working with Overloaded Constructors

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:

    class Car {
        String model;
        int doors;

        Car( String model, int doors ) {
            this.model = model;
            this.doors = doors;
            // other, complicated setup
            ...
        }

        Car( String model ) {
            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( String m ) {
        int doors = 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:

    class Car {
        ...
        final int default_doors = 4;
        ...

        Car( String m ) {
            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:

    class Car {
        ...
        static final int DEFAULT_DOORS = 4;
        ...

        Car( String m ) {
            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.

Static and Nonstatic Initializer Blocks

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:

    class MyClass {
        Properties myProps = new Properties();
        // set up myProps
        {
            myProps.put("foo", "bar");
            myProps.put("boo", "gee");
        }
        int a = 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:

    class ColorWheel {
        static Hashtable colors = new Hashtable();

        // set up colors
        static {
            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.

Get Learning Java, 4th Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.