Concepts / Creating Objects with Constructors

Creating Objects with Constructors

An instance variable can receive an initial value directly in its declaration, such as public int die1 = 3;, and that value is the starting value of each new object.

  • Programming

Why New Objects Need Starting Values

Every object you create begins its life with some values stored in its fields. Consider a program that models a pair of dice. Each pair needs two numbers, one for each die, and those numbers must already have a value the moment the pair exists. Someone reading the class has to know what those starting values are, and someone debugging a program that seems to show the wrong number has to know where those values came from.

What do you think happens?

A PairOfDice is created with the statement new PairOfDice(), and its class declares int die1 with no initializer. Before anything else runs, what value does die1 hold?

  • 0

  • 1

  • It has no value until roll() is called

  • null

Reveal answer
Answer:

0

A numeric instance variable with no initializer receives the default value zero when the object is created. Nothing has to call roll() first for that default to exist.

The answer comes straight from the declarations. The number after the equals sign, when one is written, is the value the field holds when the object is created. Nothing in the class changes those values until a method assigns something new to them. In the dice class this article follows, the roll() method is the only code that does so, and it runs only when someone calls it.

Committing to a Picture of a Variable

Many beginners picture a variable as a labeled box that holds an object. When they write a line that creates a Student, they imagine the Student sitting inside a box named std. This picture feels natural, and it is the first thing this section asks you to test. The picture fails in exactly the places where programs behave in surprising ways, so it is worth checking it before relying on any other model.

What do you think happens?

A variable std has been declared with the class type Student, and the program runs the single statement std = new Student(); What does std hold once that line has finished? Choose an answer now, even if you are unsure.

  • The Student object itself, stored inside std

  • A reference that leads to the Student object

Reveal answer
Answer:

A reference that leads to the Student object

The variable std holds a reference, also called a pointer, to the object. The object itself is a separate thing and is not stored inside the variable.

If your prediction differed from the reveal, the step where it diverged is the one to examine. The next sections show what the new operator does at that step, and then how the variable and the object relate to each other once the statement is done.

Initializers in Declarations

An instance variable can receive an initial value in its declaration, in the same way any other variable can. You write the type, the name, an equals sign, and the starting value. The statement public int die1 = 3; declares an instance variable named die1 that holds an int, and it fixes that variable's starting value at 3. Because the value is part of the declaration itself, a reader can see the starting state of the field without searching through the constructor or other methods.

java
Notes on the code
  • die1 and die2 each have an initializer, so each object starts with 3 and 4.
  • roll() is the only code that changes the values, and it runs only when a caller invokes it.

An initializer is not a one-time setup for the whole class. Every time a new PairOfDice is created, its dice are set to their starting values. Because initialization runs for each new object, each pair gets its own copy of the instance variables, and different pairs can hold different values. If an initializer computed random numbers, every new pair would receive a fresh set of random starting values.

Default Values When No Initializer Is Written

A declaration does not have to include an initializer. If you leave one out, Java still gives the instance variable a starting value automatically, and the value depends on the variable's type. Numeric types such as int and double start at zero. A boolean starts at false. A char starts at the Unicode character with code number zero. A variable of object type starts at null, and this includes String, since String is an object type.

Instance variable typeDefault when no initializer is written
Numeric types such as int and double0
booleanfalse
charthe Unicode character with code number zero
Object types, including Stringnull

Default starting values Java supplies for instance variables that have no initializer

java
Output
count: 0
ready: false
grade code: 0
label: null
Notes on the code
  • The char is cast to int so its numeric code can be printed, since a raw zero character is hard to read on screen.
  • The String field prints null, not an empty string, because no initializer was written.

Initializer Versus Default at a Glance

The two sources of a starting value look similar in a program's output, but they come from different places in the source code. Comparing them side by side makes it clear which one to look for when a value is surprising.

SituationWhere the starting value comes fromExampleStarting value of die1
Declaration has an initializerThe value written after the equals signpublic int die1 = 3;3
Declaration has no initializerThe default for the variable's typepublic int die1;0

The same field with and without an initializer

Finding the Source of a Surprising Value

The worked example separates two moments that are easy to confuse: the moment an object begins and the moment its values change. Initialization belongs to the first moment, and assignments inside methods belong to the second. When a value looks wrong, the useful question is which of those moments produced it.

Worked example

A PairOfDice that shows an unexpected die value

A program creates a PairOfDice with the class shown earlier, but the first printed value of die2 is not 4. The declaration is public int die2 = 4; but the output shows a different number. Where did the value come from?

  1. 1
    Check whether the declaration has an initializer

    The declaration public int die2 = 4; has an initializer, so the starting value of die2 should be 4 at creation.

  2. 2
    Check whether the code printed a different field

    Confirm the printed line reads die2 and not die1 or a copy of the value from another object.

  3. 3
    Check whether later code assigned a different value

    Look for assignments to die2 that run before the print. The roll() method is the only code that assigns die2 here, so if roll() ran first, the new value is the expected cause.

  4. 4
    Decide which moment produced the value

    If no assignment ran, the value is the initializer's 4. If roll() ran, the value came from a method assignment, not from initialization.

Result

The unexpected value came from a later assignment inside roll(), not from the declaration. A declaration with an initializer always starts the object at that value, so the cause must be a later assignment.

What the new Operator Does

The new operator is the mechanism that brings an object into existence in a program. When you write new followed by a class name and parentheses, the operator does two things. It creates an object that is an instance of that class, and it returns a reference to that object. The returned reference is what gets stored. The created object is a separate thing from the reference that points to it.

The operator also does more work behind the scenes. It calls a special subroutine defined in the class, called a constructor. The constructor is the part of the class responsible for setting up a new instance. You do not call the constructor as a separate step; the new operator invokes it as part of creating the object.

new operatorStudent constructornew Student objectvariable std1. calls constructor2. sets up the object3. reference returned4. reference stored
In what order do the steps happen when new is evaluated, and which value is finally returned and stored in the variable?

Read the diagram as a sequence of messages rather than as a single action. The new operator first hands control to the constructor, the constructor finishes setting up the object, and only then does a reference come back to be stored in the variable. The variable is the last participant in the chain, and it receives a reference, not the object.

Reference Versus Object

Now the picture can be corrected. The variable std holds a reference, which is also called a pointer, to the object. The object itself lives in the heap. The variable and the object are two separate things joined by a link. The variable is not a container that the object sits inside.

stdreference variableStudent objectlives in the heaprefers to
When std = new Student() has finished, where does the object live relative to the variable std?

The arrow in the diagram carries the important idea. It shows that std refers to the object. The object is not drawn inside the variable box, because the variable does not contain it. If you find yourself saying that the object is stored in std, replace that sentence with one saying that std refers to the object.

Tracing Creation Statements in Sequence

Once the reference model is clear, a short sequence of creation statements can be traced one line at a time. For each line, ask two questions: which object did new just create, and which variables now refer to that object? The following sequence assumes a Student class exists.

java
Notes on the code
  • Line 1 creates Object 1 and makes a refer to it.
  • Line 2 creates a second object, Object 2, and makes b refer to it.
  • Line 3 copies the reference held by a into c, so c and a now refer to the same object.
No variables yetbefore line 1a: Object 1after line 1a: Object 1, b:Object 2after line 2a and c: Object 1, b:Object 2after line 3line 1line 2line 3
After each line of the sequence, which object does each variable refer to, and which variables now share an object?

After line 3, two variables share one object and the other variable refers to a different object. Changing the object through c would also be visible through a, because both names lead to the same object. Only the first two lines create objects; line 3 creates no new object and only copies a reference.

Mistakes With Starting Values and References

  • Common mistake

    Assuming a String field starts as an empty string

    What it looks like

    A String label with no initializer is printed and shows null.

    Why it fails

    Object types, including String, default to null when no initializer is written.

    Do this instead

    Expect null for an uninitialized String, or write an explicit initializer if an empty string is needed.

  • Common mistake

    Believing the object is stored inside the variable

    What it looks like
    Saying that the Student object is stored in std after std = new Student();
    Why it fails

    The variable holds a reference that points to the object, and the object lives in the heap.

    Do this instead

    Say that std refers to the object.

  • Common mistake

    Expecting initialization to run once for the whole class

    What it looks like

    Assuming two PairOfDice objects share the same die1 value after one has been rolled.

    Why it fails

    Instance variable initialization runs for every new object, so each object has its own copy of the fields.

    Do this instead

    Treat each object's starting values as separate, and reserve the once-only pattern for static variables.

  • Common mistake

    Calling the constructor as a separate step

    Why it fails

    The new operator invokes the constructor as part of creating the object.

Practice Tracing Starting Values

MEDIUM

A class declares int a = 5; int b; boolean flag; String name; and no constructor is written. An object of this class is created, and then no method is called. Predict the starting value of each of the four fields. Then trace this sequence: first PairOfDice p = new PairOfDice(); then PairOfDice q = new PairOfDice(); then q = p;. After those lines, state which object each variable refers to and whether p and q share an object.

Hints
  • For each field, first ask whether the declaration has an initializer. If it does, the starting value is the value after the equals sign.
  • If there is no initializer, use the default for the type: 0 for numeric, false for boolean, the zero-code char, and null for an object type such as String.
  • Each new expression creates a separate object. An assignment of one reference variable to another copies the reference, not the object.

Key Takeaways

  • An instance variable's initializer, such as public int die1 = 3;, sets the starting value of that field in every new object, and initialization runs once per object.
  • With no initializer, Java supplies 0 for numeric types, false for boolean, the zero-code char, and null for object types including String.
  • The new operator creates an object, invokes its constructor as part of that creation, and returns a reference that is stored in the variable.
  • A variable holds a reference that points to the object; the object lives in the heap and is not stored inside the variable.
  • When a starting value looks wrong, check whether the declaration has an initializer and whether later code assigned a different value.