Concepts / Instance Variables and Methods

Instance Variables and Methods

A constructor has the same name as its class, has no return type, and initializes an object when it is created.

  • Programming

Starting With a Pair of Dice

This article works from a single class, PairOfDice, that represents a pair of dice. The class has two instance variables, die1 and die2, and two methods that set them in different ways. Your job is to work out which method runs when an object is created, and what values the object has at that moment. Read the class first, then make a prediction before reading the explanation that follows.

Reading the PairOfDice Class

The class below is an illustrative version of PairOfDice written to match the behavior the source describes. It declares two instance variables, die1 and die2, and defines three members: two constructors and the roll method. Read it line by line before moving on.

Code walkthrough
public class PairOfDice {
    int die1;
    int die2;

    public PairOfDice(int value1, int value2) {
        die1 = value1;
        die2 = value2;
    }

    public PairOfDice() {
        roll();
    }

    public void roll() {
        die1 = (int)(Math.random() * 6) + 1;
        die2 = (int)(Math.random() * 6) + 1;
    }
}
  1. 1
    Line 1 declares the class. Everything inside the braces belongs to PairOfDice.
  2. 2
    Lines 2 and 3 declare the instance variables die1 and die2. The class describes them; each object holds its own values.
  3. 3
    Lines 5 to 8 define the two-parameter constructor. It copies the given arguments directly into die1 and die2.
  4. 4
    Lines 10 to 13 define the no-argument constructor. Its only statement is a call to roll(), so its result comes indirectly through another method.
  5. 5
    Lines 15 to 19 define roll(). It assigns random values from 1 to 6 to both instance variables.
  • die1 and die2 are instance variables: each object created from this class gets its own pair of values.
  • The two-parameter constructor has the same name as the class and no return type.
  • The no-argument constructor has the same name as the class and no return type.
  • roll() is declared void, so it does work on the object but returns no value to the caller.

Predicting the State of a New Object

What do you think happens?

After new PairOfDice(3, 5) runs, what are the values of die1 and die2?

Reveal answer
Answer:

die1 is 3 and die2 is 5.

The two-parameter constructor assigns the values it receives directly to the instance variables. Because the arguments are fixed, the result is predictable every time.

Worked example

Tracing the state of a no-argument object

Trace what happens when the code runs new PairOfDice() and say what kind of values the object holds afterward.

  1. 1
    Identify the constructor

    No arguments are passed, so the Java compiler selects the constructor with an empty parameter list.

  2. 2
    Follow the body

    The body of that constructor is a single call to roll(). The constructor does not assign die1 or die2 itself.

  3. 3
    Run roll()

    roll() assigns a random number from 1 to 6 to die1 and another to die2.

  4. 4
    Read the final state

    The new object holds two random values from 1 to 6. Their exact values cannot be predicted in advance.

Result

The object's die1 and die2 each hold a random value from 1 to 6, which is why this constructor's result cannot be predicted exactly.

Recognizing a Constructor by Its Declaration

The name is the key identifying feature. In PairOfDice, both constructors are named PairOfDice, exactly matching the class name. A constructor is also declared without a return type. An ordinary method such as roll() is declared as void, which means it does work without returning a value to the caller. Compare the two kinds of declaration side by side.

FeatureConstructorOrdinary method
NameSame as the class name (PairOfDice)Any name chosen by the programmer (roll)
Return typeNoneDeclared, such as void
PurposeInitializes an object when it is createdPerforms work on an object that already exists
How it runsRuns when new is usedRuns when called by name on an object

Distinguishing a constructor from an ordinary method in the PairOfDice class

How Overloaded Constructors Choose Which One Runs

PairOfDice defines two constructors with the same name and different parameter lists. This is called constructor overloading. It gives the class flexibility: a caller can create a pair of dice with random starting values by passing no arguments, or with specific known values by passing two integers. The arguments in the call select which constructor runs.

new PairOfDice(...)Two integers passed?Assign die1 and die2directlytwo-parameter constructorCall roll()no-argument constructorRandom values from 1to 6yesno
When the code calls new PairOfDice, which branch runs, and where does an unexpected initial value first appear?

Read the diagram as a series of branch points. The first decision is whether the call supplies two integers. If it does, the two-parameter constructor runs and its assignments take effect directly. If it does not, the no-argument constructor runs and hands the work to roll(). The unexpected value usually appears at the branch where you expected the arguments to fix the values but the call actually took the other path, so the object holds random numbers.

Instance Variables Belong to Each Object

When an object is created using a class, that object is called an instance of the class, and you can say the object belongs to the class. The variables that the object contains are called instance variables. The word instance names the object, so an instance variable is simply a variable that lives inside one particular object.

PairOfDice classdeclares die1 and die2First objectdie1 = 3, die2 = 5Second objectdie1 = 6, die2 = 2creates instancecreates instance
When several objects come from the same class, where do their instance variable values live, and how do they stay separate?

The class is the description; the values belong to each object. The class says that every PairOfDice has a die1 and a die2, but the numbers stored in them are not part of the class. Each object keeps its own copy of those values. That is why the first object above can hold 3 and 5 while the second holds 6 and 2 at the same time, without either affecting the other.

The source makes the same point with a PlayerData class. When that class is used to create an object, the name and age variables inside that object are its instance variables. Creating a second object from PlayerData gives you a second set of name and age variables, independent of the first.

Static Methods Versus Instance Methods

The same idea extends to behavior. The subroutines an object contains are its instance methods. The source states that instance methods are the non-static methods, and that they become part of objects created from the class. Not every method belongs to objects, though. The source contrasts two kinds: static methods are part of the class, and non-static methods become part of the objects created from it. Static methods are associated with the class, not with objects.

Static methodPart of the classAssociated with theclassnot with any single objectInstance methodNon-staticPart of each objectlogically belongs toobjects made from the class
How do static methods and instance methods differ in what they are attached to?

Why an Object Only Logically Contains Its Methods

Because an instance method is logically part of an object, you can think of the object as carrying its behavior along with its data. The object contains both its instance variables and its instance methods in the logical sense. A method call on one object uses that object's instance variables, while the method code itself is defined once, as part of the class.

Mistakes Beginners Make With Object State

  • Common mistake

    Assuming a constructor with two arguments always produces random values

    What it looks like

    Reading new PairOfDice(3, 5) and expecting die1 and die2 to change each time

    Why it fails

    The two-parameter constructor assigns the given values directly, so the result is predictable.

    Do this instead

    Check which constructor the arguments select, then trace its body.

  • Common mistake

    Assuming the no-argument constructor sets the dice itself

    What it looks like

    Looking for assignments to die1 inside the no-argument constructor and finding none

    Why it fails

    Its body is a single call to roll(), which does the assignments indirectly.

    Do this instead

    Follow the call into roll() to find where the values come from.

  • Common mistake

    Thinking all instance variable values are shared by every object

    What it looks like

    Expecting a change to the first object's die1 to alter the second object's die1

    Why it fails

    Instance variables are part of the object, so each object holds its own values.

    Do this instead

    Treat the class as the description and each object as the holder of its own values.

  • Common mistake

    Treating a constructor as an ordinary method because it appears in the class body

    Why it fails

    A constructor is recognized by its name matching the class and by having no return type.

    Do this instead

    Look for the class name and the absence of a return type in the declaration.

  • Common mistake

    Confusing static methods with instance methods when deciding where a method lives

Practice: Predict Before You Run

MEDIUM

Using the PairOfDice class from this article, decide which constructor runs for new PairOfDice(6, 2) and state the values of die1 and die2 immediately afterward. Then decide what happens for new PairOfDice() and explain why its values are not predictable.

Hints
  • Count the arguments in the call to choose the constructor.
  • The two-parameter constructor copies its arguments into the instance variables.
  • The no-argument constructor calls roll(), and roll() assigns random values from 1 to 6.

Key Takeaways

  • A constructor has the same name as its class, has no return type, and initializes an object when it is created.
  • A class with several constructors uses overloading; the arguments in the call select which constructor runs.
  • Instance variables are part of each object, so every object holds its own values even though the class declares them once.
  • Instance methods, the non-static methods, logically become part of each object, but the compiled method code is defined once with the class; static methods are associated with the class itself.