Skip to main content

Safely initialising your Java objects

Posted by hellofadude on November 19, 2013 at 6:58 PM PST

The proper initialisation of objects is a concern that has primarily to do with safety in programming. In some programming languages, failing to properly initialise a variable or library component before attempting to use it can lead to very many bugs in your software that may be difficult to locate, however we are fortunate that Java takes a sensible approach to these matters.

Java provides guaranteed initialisation of objects with the use of a constructor, which is sometimes described as an implicitly static method that a class designer may provide to guarantee the initialisation of every object created. When a constructor exists for a class, Java automatically calls that constructor when an object of that class is created, before it can be used, thus guaranteeing initialisation.

Initialisation is an important technique the programmer must take care to master because it is the point at which storage is created for objects that makes them ready for use.

Class constructors

A constructor in Java, usually has the same name as the class, and can take any number of arguments as would an ordinary method. A default constructor is one that takes no arguments, and if provided is called automatically to initialise the object before it can be used; Consider the following example:-

package kingsley.java

class Table {
     public Table() {
         System.out.print("Table ");
     }
}
public class CreateTables {
     public static void main(String[] args) {
         for(int i = 0; i < 5; i++)
             new Table();
     }
}
/* Output
Table Table Table Table Table
*//

As you can see, constructor names do not exactly follow the normal naming convention of making the first letter of all method names lowercase, because the constructor name must match the class exactly. In the above example, the default constructor Table(), prints "Table" to screen when we create a Table object using the new keyword. If you create a class with no constructors, Java automatically creates a default constructor for you, however if you define any constructors for a class, with or without arguments, and attempt to call a constructor that you have not defined, the compiler will not synthesize one for you, more likely, that it might complain about not finding a match for the method you have called.

Constructors can, like regular methods, also accept any number of arguments; for instance, we can modify our Table constructor to accept some argument like so:-

package kingsley.java; 

class Table {
     int i;
     public Table(int i) {
         this.i = i;
         System.out.print("Table" + i + " ");
     }
}
public class Carpenter {
     static void makeTable(int i) {
       new Table(i);
     }
     public static void main(String[] args) {
         for(int i = 0; i < 5; i++)
             Carpenter.makeTable(i);
     }
}
/* Output
Table0 Table1 Table2 Table3 Table4
*//

Constructor arguments allow you to provide parameters for the initialisation of an object. The above example takes an int argument and provides a way for you to distinguish between Table objects.

It is possible for a class to have more than one constructor in circumstances where you may want to create an object in more than one way. Method overloading is a technique that allows the same method name to be used with different argument types, thus you could have a class that looks like this:-

package kingsley.java;

class Car {
     int speed;
     Car() {
         System.out.print("Car is stopped");
         speed = 0;
     }
     Car(int speed) {
         this.speed = speed;
         System.out.println("Car is travelling at " + speed + " mph" );
     }
}
public class PoliceSpeedoMeterClock {
     public static void main(String[] args) {
         for(int i = 1; i < 6; i++) {
             Car c = new Car(i);
         }
         new Car();
     }
}
/* Output
Car is travelling at 1 mph
Car is travelling at 2 mph
Car is travelling at 3 mph
Car is travelling at 4 mph
Car is travelling at 5 mph
Car is stopped
*//

The Car class demonstrates the use of overloaded constructors. The first constructor is the default constructor that prints "Car is stopped" to screen. The second overloaded constructor uses an int variable to simulate the speed at which the car is currently travelling. Also note how this is achieved with the use of a for loop in addition to the fact that both constructors have the same name, which is of course the same as that of the class.

Overloading methods

Method overloading is not limited by any means to just constructors alone, but can be applied to regular methods within a class sameway. Java is able to distinguish between overloaded methods by their list of argument types which must be unique to each overloaded method, and even the ordering of such a list is sufficient for Java to be able to distinguish between two or more overloaded methods. Here is an example of overloaded methods using primitive types:-

package kingsley.java;

public class Dog {
     void Bark(char c) {
         System.out.println("whine whine");
     }
     void Bark(int i, float f) {
         System.out.println("woof woof");
     }
     void Bark(float f, int i) {
         System.out.println("ruff ruff");
     }
     void Bark(double d) {
         System.out.println("howl howl ");
     }
     public static void main(String[] args) {
         Dog d = new Dog();
         d.Bark('a');
         d.Bark(2, 2.1f);
         d.Bark(2.1f, 4);
         d.Bark(3.2);
     }
}
/* Output
whine whine
woof woof
ruff ruff
howl howl
*//

The Bark() method is overloaded using primitive types and even though we have not defined any constructors, it is obvious from this example that Java automatically creates a default constructor. Each method prints a different type of dog bark to screen depending on its arguments. Note how the arguments to the second and third overloaded Bark() methods are exactly the same, yet Java is able to distinguish these because the ordering of the arguments in both methods are different.

It is worth bearing in mind that when you use primitives with overloaded methods or methods in general, a primitive argument of a smaller type can be automatically promoted to a larger type without too much hassle. However when you have a larger argument than is expected by the method, you will be required to perform a narrowing conversion with a cast to avoid a compiler error:-

package kingsley.java;

public class Race {
     void start(int i) {
         System.out.println("int " + i);
     }
     void start(double d) {
         System.out.println("double " + d);
     }
     void end(short s) {
         System.out.println("short " + s);
     }
     public static void main(String[] args) {
         Race f1 = new Race();
         f1.start(new Character('a'));
         f1.start(new Byte("5"));
         f1.start(new Short("8"));
         f1.start(new Float(1.5));
         f1.start(new Long(23000000));
         char c = 'a';
         int x = 10;
         float f = 1.3f;
         long l = 23000000L;
         f1.end((short)c);
         f1.end((short)x);
         f1.end((short)f);
         f1.end((short)l);
     }
}
/* Output
int 97
int 5
int 8
double 1.5
double 2.3E7
short 97
short 10
short 1
short -3136
*//

In this example, the Race class defines an overloaded start() method and an end() method. We test both methods against a number of primitive types in the main. The overloaded start() method is able to make automatic conversions depending on the given argument; The smaller arguments are promoted to type int while the larger arguments like float and long are converted to double. The end() method on the other hand requires the use of a narrowing conversion from various larger arguments to short primitive type.

this keyword

The this keyword is a useful mechanism reserved for those occasions when you wish to reference the current object from within a non-static method of another class. In other words, as your programs get larger and more complicated, it can become a daunting task to keep track of objects you create. The this keyword allows you to reference the current object for which a method has been called. It is most often used in return statements to return a reference to the current object.
For instance, here is an example that uses the this keyword to pass the current Item object to the scan() method of the Cashier class:-

package kingsley.java;

class ShoppingCart {
     Item[] item = new Item[1];
     public ShoppingCart add(Item it) {
         for(int i = 0; i < item.length; i++)
             this.item[i] = it;
         System.out.print(" added to cart" + "\n");
         return this;
     }
}
class Item {
     String name;

     Item(String s) {
         this.name = s;
     }
     Item buy() {
         return Cashier.scan(this);
     }
}
public class Cashier {
     static Item scan(Item it) {
         System.out.print(it.name + " paid -");
         return it;
     }
     public static void main(String[] args) {
         ShoppingCart sc = new ShoppingCart().add(new Item("cofee").buy());
         sc.add(new Item("Bread").buy());
     }
}
/* Output
cofee paid - added to cart
Bread paid - added to cart
*//

The Item class in this example uses the this keyword to pass the current Item object to the Cashier.scan() method, which can be thought of as a foreign utility method to the class itself. This style of programming is common when you need an external method that can be applied across many different classes.
Note how we use a constructor to initialise the String data member - name, of the Item class in a manner that provides us with a way to uniquely identify each item object added to the cart.

The this keyword takes on a different meaning however when used with constructors. In those instances, it makes an explicit call to the constructor that matches the argument list. Here is an example:-

package osime.java;

public class Bird {
     int birdCount = 30;
     String s = "Pigeons";

     Bird (int number) {
         this("Pigeons");
         birdCount = number;
         System.out.println("Bird constructor w/ int arg");
     }
     Bird(String s){
         System.out.println("Bird constructor w/ String args");
         this.s = s;
     }
     Bird(String s, int number) {
         this(number);
         this.s = s;
         System.out.println("Bird constructor, w/ String & int args");
     }
     Bird() {
         this("Swallows", 10);
         System.out.println("Default constructor, no args");
     }
     void printBirdMembers() {
         //Error can not use this from non-constructor
         //!this(10);
         System.out.println("Bird count = " + birdCount + " s = " + s);
     }
     public static void main(String[] args) {
         Bird b = new Bird();
         b.printBirdMembers();
     }
}
/* Output
Bird constructor w/ String args
Bird constructor w/ int arg
Bird constructor, w/ String & int args
Default constructor, no args
Bird count = 10 s = Swallows
*//

In this example, we create a Bird class with an overloaded constructor. Notice how the this keyword is used from within each constructor to call another constructor. In the default Bird() constructor, the call to the overloaded constructor with String & int arguments using the this keyword, makes a call to the overloaded Bird constructor with int argument, which in turn makes a call to the String argument constructor in a kind of series of recursive method call. Also note that when using the this keyword from within constructors, the call to the constructor must be the first thing you do or else you get an error. Further more in the printBirdCount() method, you can see how it is not possible to make a call to a constructor using the this keyword from within a non-constructor.

It is important to make the connection here between the use of the this keyword and static methods; essentially, the this keyword can not be applied to static methods since static methods are in a sense global methods i.e. they can be called for the class but not for individual objects unlike the this keyword which when used refer specifically to the current object.

Member Initialisation

Java ensures field variables within a class are initialised to default values before they are used. This guarantee applies equally to primitives as well as objects:-

package kingsley.osime;

public class DefaultValues {
     static byte b;
     static short s;
     static int i;
     static long l;
     static float f;
     static double d;
     static boolean bl;
     static char c;
     static DefaultValues reference;
     static void printDefaultValues() {
         System.out.println("byte = " + b);
         System.out.println("short = " + s);
         System.out.println("int = " + i);
         System.out.println("long = " + l);
         System.out.println("float = " + f);
         System.out.println("double = " + d);
         System.out.println("boolean = " + bl);
         System.out.println("char = [ "  +  c  + "]");
         System.out.println("DefaultValues = " + reference);
     }
     public static void main(String[] args) {
         DefaultValues.printDefaultValues();

     }

}
/* Output
byte = 0
short = 0
int = 0
long = 0
float = 0.0
double = 0.0
boolean = false
char = [
DefaultValues = null
*//

Even as we have not initialised these variables, Java is able to automatically initialise them to default values. With non-primitives like the DefaultValues object, Java automatically initialises these to null.

It is noteworthy that this guarantee of initialising variables does not apply to local variables, which are variables created within a method. If you were to try to call a method from an uninitialised local variable, you will be certain to receive a compile time error.

We could initialise our variables directly by assigning values at the point of creation like so:-

public class DefaultValues {
      byte b = 30;
      short s = 0xff;
      int i = 10;
      long l = 1;
      float f = 2.1f;
      double d = 3.14;
      boolean bl = true;
      char c = 'a';
}

The same method could be used to initialise non-primitive objects; for instance, we could create and initialise a Data object like this:-

class Data {}

public class System {
       Data d = new Data();
}

A variable can be initialised in any number of ways, including by calling a method that returns some value that is of the same type as the variable; for instance you could say:-

public class Init {
     double d = f();
     double f() { return 2.3; }
}

As you can see the f() method returns a value of type double which is of course the same type as the variable d for which it is called. Such methods may also have arguments, providing those arguments belong to class members that have previously been initialised. When you try to initialise a variable with a method that takes arguments that are yet to be initialised, the compiler complains about forward referencing, which is basically a warning of an uninitialised variable.

Order of initialisation

Variables within a class get initialised in the order in which they are defined within the class. Variable definitions may be scattered throughout and in between method definitions, but they are always initialised before any methods are called, including even the constructor.

At this point it is necessary that we make a distinction between static and non-static fields as these definitions have implications for when and how defined variables are initialised. The following example demonstrates the proper order of initialisation in Java:-

package kingsley.osime;

class ClothingItem {
     ClothingItem(int number) {
         System.out.println("ClothingItem (" + number + ")");
     }
     void c1(int number) {
         System.out.println("c1 (" + number + ")");
     }
}
class Suitcase {
     static ClothingItem cl1 = new ClothingItem(1);
     Suitcase() {
         System.out.println("Suitcase ()");
         cl2.c1(1);
     }
     void s1(int number) {
         System.out.println("s1 (" + number + ")");
     }
     ClothingItem cl2 = new ClothingItem(2);
}
public class OrderOfInit {

     public static void main(String[] args) {
        System.out.println("Creating new Suitcase in main");
         new Suitcase();
         System.out.println("Creating new Suitcase in main");
         new Suitcase();
         suitcase.s1(1);
     }
     static Suitcase suitcase = new Suitcase();
     static ClothingItem item = new ClothingItem(3);
}
/* Output
ClothingItem (1)
ClothingItem (2)
Suitcase ()
c1 (1)
ClothingItem (3)
Creating new Suitcase in main
ClothingItem (2)
Suitcase ()
c1 (1)
Creating new Suitcase in main
ClothingItem (2)
Suitcase ()
c1 (1)
s1 (1)
*//

The Suitcase class contains a static and non-static member of the ClothingItem class and along with the OrderOfInit class allows you to observe the proper order of initialisation. As you may observe from the example, the static members of the OrderOfInit class are initialised first which in turn causes those classes to be loaded and since the Suitcase class contain static ClothingItem members (1) and (2) those are first initialised before making a call to the Suitcase constructor and then the static ClothingItem object (3) is also created. All through this, the main() method of the OrderOfInit class is yet to be called.

Note that static members are only initialised once, when they are first accessed or when an object of the class is first created, but non-static members are re-initialised everytime they are accessed.

From this we can surmise the order of initialisation as statics first, if they are not initialised already, followed by non-static objects before any class constructors are called. When inheritance is involved, the process does get a little bit more complicated.

Initialising Arrays

Arrays are containers that hold a sequence of objects or primitive types, packaged under an identifier referencing the same type. Arrays may be of any length and are defined using the square-brackets [] indexing operator, for instance, to create an array of type int you would write something like this:-

int[] x1;

Here, the variable x1, is declared as an array of type int. It is also possible though unconventional for the square-brackets to be located after the identifier like so:-

int x1[];

At this point, all you have is a reference to an array and therefore no space has been allocated to the array object itself. It is often necessary to write an initialisation expression in order to create storage for an array, one way to do this would be to use the new keyword like so:-

int[] x1 = new int[20];

Note that using the new keyword with primitives will only work when applied in the context of an array. The above expression creates an array with enough storage for 20 elements and assigns the array to the x1 variable. We could have also used a special kind of initialisation that encloses a set of values within curly braces like so:-

int[] x1 = { 2, 4, 6, 8, 10};

Here the x1 array contains the values 2, 4, 6, 8 and 10 all of which comprise its elements. Each element can be referenced by means of an index that begin at 0 up to the number of elements in your array. For instance, in our x1 array, the element at index 0 is 2, element at index number 1 is 4, at index number 2 is 6 and so on.

All arrays regardless of type have an intrinsic length member that allow you to query the number of elements defined within the array. Elements start from 0 and the largest element you can index is equal to length - 1. For instance to find out the number of elements contained within the x1 array you would write:-

int size = x1.length;

If you were to call a method to print out the value of the size variable, the number 5 would be returned. Here is an example that uses a for loop to iterate through an array of type while populating it:-

package kingsley.java;

import java.util.Random;

public class InitArray {
     static int[] x1 = new int[5];
     public static void main(String[] args) {
         Random rand = new Random(10);
         for(int i = 0; i < x1.length; i++) {
             x1[i] = rand.nextInt(20);
             System.out.println("Element at " + i + " is " + x1[i]);
         }
     }
}
/* Output
Element at 0 is 13
Element at 1 is 0
Element at 2 is 13
Element at 3 is 10
Element at 4 is 6
*//

What we see here is a random object number generator rand, using the nextInt() method, available to all random objects, to populate the x1[] int array, whose current elements are being referenced by the variable quantity i being used to control the loop. Note that the x1[] array was initialised prior to this and is declared as static. If you fail initialise the array variable prior to using it, you will get a compiler error about an uninitialised variable, likewise, failure to declare the x1 object as static, risks producing an error about trying to reference a non-static method from a static one, which if you recall is what the main() method is.