Skip to main content
Logo image

Java, Java, Java: Object-Oriented Problem Solving, 2024E

Section 3.3 Constructors

In the previous section, we looked at several examples of mutator methods that change the values of private instance variables of an object. It is possible to define mutator methods to set the initial values of instance variables after an object is created, but initial values can also be set by constructors.
A constructor is used to create an instance (or object) of a class and to assign initial values to instance variables. A constructor declaration looks just like a method definition except:
  1. it must have the same name as the class,
  2. and it does not have a return type.
Unlike the class level variables and methods of a class, constructors are not considered members of the class. Therefore, they are not inherited by a class’s subclasses. Constructors usually are public. Here is a simple constructor for our OneRowNim class. Notice there is no return type void in between the public and the classname.
public OneRowNim()
{   nSticks = 7;
    player = 1;
}
This constructor merely sets the initial values of the instance variables, nSticks and player. In our current version of OneRowNim these variables are given initial values by using initializer statements when they are first declared:
private int nSticks = 7;
private int player = 1;
So we now have two ways to initialize a class’s instance variables. In the OneRowNim class it doesn’t really matter which way we do it. However, the constructor provides more flexibility because it allows the state of the object to be initialized at runtime. Of course, it would be somewhat redundant (though permissible) to initialize the same variable twice, once when it is declared and again in the constructor, so we should choose one or the other way to do this. For now, let’s stick with initializing the instance variables when they are declared.
A constructor cannot return a value and, therefore, its declaration cannot include a return type. Because they cannot return values, constructors cannot be invoked by a regular method invocation. Instead, constructors are invoked as part of an instance creation expression when instance objects are created. An instance creation expression involves the keyword new followed by the constructor invocation:
OneRowNim game // Declare
    = new OneRowNim(); // and instantiate game1
OneRowNim game2 // Declare
    = new OneRowNim(); // and instantiate game2
Note here that we have combined variable declaration and instantiation into a single statement, whereas in some previous examples we used separate declaration and instantiation statements. Either way is acceptable.
Constructors should be used to perform the necessary initialization operations during object creation. In the case of a OneRowNim object, what initializations could be performed? One initialization that would seem appropriate is to initialize the initial number of sticks to a number specified. In order to do this, we would need a constructor with a single int parameter:
public OneRowNim(int sticks)
{   nSticks = sticks;
}
Now that we have this constructor we can use it when we create instances of OneRowNim:
OneRowNim game1 = new OneRowNim(21);
OneRowNim game2 = new OneRowNim(13);
The effect of these statements is the same as if we had used the setSticks() method that was discussed briefly in the last section. The difference is that we can now set the number of sticks when we create the object.
Should we keep the preceding constructor, or keep the setSticks() method or keep both in our class definition? The constructor can only be invoked as part of a new statement when the object is created, but the setSticks() method could be called anytime we want. In many cases, having redundant methods for doing the same task in different ways would be an asset, because it allows for more flexibility in how the class could be used. However, for a game like One Row Nim, a major concern is that the two instance variables get changed only in a manner consistent with the rules for One Row Nim. The best way to guarantee this is to have takeSticks() as the only method that changes the instance variables nSticks and player. The only time that it should be possible to set the number of sticks for a game is when a constructor is used to create a new instance of OneRowNim.

Exercises 3.3.1 Self-Study Exercises

1. Constructor Definition Bug.

What’s wrong with the following constructor definition?
public void OneRowNim(int sticks)
{   nSticks = sticks;
}
Hint.
What is void in the header?
In the active code below or in your own development environment, change the OneRowNim(int sticks) constructor so that it sets the number of sticks and also have it also set player two as the player who takes the first turn. Also, write a constructor that takes 2 parameters to set the number of sticks and to set the player.

2. OneRowNim Constructors.

Change the OneRowNim(int sticks) constructor to also set the player to 2. Write another constructor that takes 2 parameters to set the number of sticks and to set the player.

Subsection 3.3.2 Default Constructors

Java automatically provides a default constructor when a class does not contain a constructor.
The default constructor’s role is simply to create an instance (an object) of that class. It takes no parameters. In terms of what it does, the default constructor for OneRowNim would be equivalent to a public constructor method with an empty body:
public OneRowNim() { }
This explains why the following statement was valid when a class definition of OneRowNim contained no explicit definition of a constructor:
OneRowNim game = new OneRowNim();

Subsection 3.3.3 Constructor Overloading and Method Signatures

It is often quite useful to have more than one constructor for a given class. For example, consider the following two OneRowNim constructors:
public OneRowNim() {} // Constructor #1
public OneRowNim(int sticks)   // Constructor #2
{   nSticks = sticks;
}
The first is an explicit representation of the default constructor. The second is the constructor we defined earlier to initialize the number of sticks in a OneRowNim object. Having multiple constructors lends flexibility to the design of a class. In this case, the first constructor merely accepts OneRowNim’s default initial state. The second enables the user to initialize the number of sticks to something other than the default value.
In Java, as in some other programming languages, when two different methods have the same name, it is known as method overloading. In this case, OneRowNim is used as the name for two distinct constructor methods. What distinguishes one constructor from another is its signature, which consists of its name together with the number and types of formal parameters it takes. Thus, our OneRowNim constructors have the following distinct signatures:
OneRowNim()
OneRowNim(int)
Both have the same name, but the first takes no parameters, whereas the second takes a single int parameter.
The same point applies to methods in general. Two methods can have the same name as long as they have distinct method signatures. A class may not contain two methods with the same signature, but it may contain several methods with the same name, provided each has a distinct signature.
There is no limit to the amount of overloading that can be done in designing constructors and methods. The only restriction is that each method have a distinct signature. For example, suppose in addition to the two constructors we have already defined, we want a constructor that would let us set both the number of sticks and the player who starts first. The following constructor will do what we want:
public OneRowNim(int sticks, int starter)
{   nSticks = sticks; // Set the number of sticks
    player = starter; // Set who starts
}
When calling this constructor, we would have to take care to pass the number of sticks as the value of the first argument and either 1 or 2 as the value of the second argument:
OneRowNim game3 = new OneRowNim(14, 2);
OneRowNim game4 = new OneRowNim(31, 1);
If we mistakenly reversed 14 and 2 in the first of these statements, we would end up with a OneRowNim game that starts with 2 sticks and has player 14 as the player with the first move.
We have now defined three constructor methods for the OneRowNim class. Each constructor has the name OneRowNim, but each has a distinct signature:
OneRowNim()
OneRowNim(int)
OneRowNim(int, int)

Subsection 3.3.4 Constructor Invocation

A constructor method is invoked only as part of a new expression when an instance object is first created. Each of these is a valid invocation of a OneRowNim constructor:
// Default constructor
OneRowNim game1 = new OneRowNim();
                  // Sets number of sticks
OneRowNim game2 = new OneRowNim(21);
                  // Sets both instance variables
OneRowNim game3 = new OneRowNim(19, 2);
The following constructor invocations are invalid because there are no matching constructor definitions:
// No matching constructors
OneRowNim game4 = new OneRowNim("21");
OneRowNim game5 = new OneRowNim(12, 2, 5);
In the first case, there is no constructor method that takes a String parameter, so there’s no matching constructor. In the second case, there is no constructor that takes three int arguments. In both cases, the Java compiler would complain that there is no constructor method that matches the invocation.
You have attempted of activities on this page.