[ Team LiB ] Previous Section Next Section

4.5 Initialization

All chained constructors are automatically called when creating an object with new. Chaining more constructors for a particular object causes extra overhead at object creation, as does initializing instance variables more than once. Be aware of the default values that Java initializes variables to:

  • null for objects

  • 0 for integer types of all lengths (byte, char, short, int, long)

  • 0.0 for float types (float and double)

  • false for booleans

There is no need to reinitialize these values in the constructor (although an optimizing compiler should be able to eliminate the redundant statement). Generalizing this point: if you can identify that the creation of a particular object is a bottleneck, either because it takes too long or because a great many of those objects are being created, you should check the constructor hierarchy to eliminate any multiple initializations to instance variables.

You can avoid constructors by unmarshalling objects from a serialized stream because deserialization does not use constructors. However, serializing and deserializing objects is a CPU-intensive procedure and is unlikely to speed up your application. There is another way to avoid constructors when creating objects, namely by creating a clone( ) of an object. You can create new instances of classes that implement the Cloneable interface using the clone( ) method. These new instances do not call any class constructor, thus allowing you to avoid the constructor initializations. Cloning does not save a lot of time because the main overhead in creating an object is in the creation, not the initialization. However, when there are extensive initializations or many objects generated from a class with some significant initialization, this technique can help.

If you have followed the factory design pattern,[6] it is relatively simple to reimplement the original factory method to use a clone.

[6] The factory design pattern recommends that object creation be centralized in a particular factory method. So instead of directly calling new Something( ) in the code to create an instance of the Something class, you call a method such as SomethingFactory.getNewSomething( ), which creates and returns a new instance of the Something class. This is actually detrimental for performance, as there is the overhead of an extra method call for every object creation, but the pattern does provide more flexibility when it comes to tuning. My inclination is to use the factory pattern. If you identify a particular factory method as a bottleneck when performance tuning, you can relatively easily inline that method using a preprocessor.

For example, the original factory method can be defined similar to:

public static Something getNewSomething(  )
{
  return new Something(  );
}

The replaced implementation that uses cloning looks like:

private static Something MASTER_Something = new Something(  );
public static Something getNewSomething(  )
{
  return (Something) MASTER_Something.clone(  );
}

If you have not followed the factory design pattern, you may need to track down all calls that create a new instance of the relevant class and replace those calls. Note that the cloned object is still initialized, but the initialization is not the constructor initialization. Instead, the initialization consists of assigning exactly once to each instance variable of the new (cloned) object, using the instance variables of the object being cloned.

Java arrays all support cloning. This allows you to manage a similar trick when it comes to initializing arrays. But first let's see why you would want to clone an array for performance reasons.

When you create an array in code, using the curly braces to assign a newly created array to an array variable like this:

int[  ] array1 = {1,2,3,4,5,6,7,8,9};

you might imagine that the compiler creates an array in the compiled file, leaving a nice structure to be pulled into memory. In fact, this is not what happens. The array is still created at runtime, with all the elements initialized then. Because of this, you should specify arrays just once, probably as a static, and assign that array as required. In most cases this is enough, and there is nothing further to improve on because the array is created just once. But sometimes you have a routine for which you want to create a new array each time you execute it. In this case, the complexity of the array determines how efficient the array creation is. If the array is quite complex, it is faster to hold a reference copy and clone that reference than it is to create a new array. For instance, the array example shown previously as array1 is simple and therefore faster to create, as shown in that example. But the following more complex array, array2, is faster to create as a cloned array:

static int[  ] Ref_array1 = {1,2,3,4,5,6,7,8,9};
static int[  ][  ] Ref_array2 = {{1,2},{3,4},{5,6},{7,8}};
  
int[  ] array1 = {1,2,3,4,5,6,7,8,9};         //faster than cloning
int[  ] array1 = (int[  ]) Ref_array1.clone(  );  //slower than initializing
  
int[  ][  ] array2 = {{1,2},{3,4},{5,6},{7,8}}; //slower than cloning
int[  ][  ] array2 = (int[  ][  ]) Ref_array2.clone(  );//faster than initializing
    Previous Section Next Section