Static Variables and Static Initialization
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.
Why Starting Values Matter
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 so does anyone debugging a program that seems to show the wrong number. In this article we follow one class from its source code through to the values an object actually holds, and we look at where those values come from.
What do you think happens?
A class declares public int count; and gives it no initializer. What value does a new object start with in count?
Reveal answer
0
When a declaration has no initializer, Java supplies a default. Numeric types such as int start at zero.
Tracing a New PairOfDice Object
The answer comes straight from the declarations. Here is a small class that models a pair of dice. Each die is an instance variable of type int, and each declaration includes an initializer after the equals sign. The roll() method is the only code in this class that changes the dice, and it runs only when someone calls it. The assignments inside roll() are example code written for this article.
When new PairOfDice() runs, the object is created with die1 holding 3 and die2 holding 4. Nothing else has to happen first. The values are already in place before any method is called. The diagram below shows the fields inside that new object. Its nodes are the two instance variables and the object that holds them.
The two fields are part of the object, so they exist for as long as the object does. Their starting values are fixed by the declarations, not by the constructor or by any method. A reader of the class can therefore see the starting state of each field without searching through other code.
How an Initializer Sets the Starting Value
An explicit initializer is the value written after the equals sign in an instance variable declaration. The field holds that value when each new object is created.
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.
An initializer is not a one-time setup for the whole class. Every time a new PairOfDice is created, the dice are set to their starting values. Because the 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.
The before version has no initializers, so Java gives each int field the default zero. The after version has initializers, so the same two fields start at 3 and 4. The fields have the same names and the same type. Only the starting value differs, and the initializer is what makes the difference.
Default Values by Type
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. The table lists the rules for the four kinds of field covered in the source.
| Field type | Default starting value | Notes |
|---|---|---|
| Numeric types such as int and double | 0 | The zero of that numeric type |
| boolean | false | Not written as a string |
| char | The Unicode character with code number zero | Cast to int to see its numeric code |
| Object types, including String | null | A String field is null, not an empty string |
Default starting values Java assigns to instance variables with no initializer
The object-type row deserves attention. String is an object type, so a String field with no initializer begins as null. It does not begin as an empty string, which is a different value that a program must write explicitly.
Worked Example: Reading Default Values
The next program declares four fields with no initializers and prints the value each one holds. Casting the char to int is a way to show its numeric code, since a raw zero character would be hard to read on screen.
0
false
0
null- count has no initializer, so it holds the int default 0
- flag has no initializer, so it holds the boolean default false
- letter holds the char with code number zero, and casting to int prints that code as 0
- name is a String, an object type, so it holds null rather than an empty string
Predicting Four Printed Lines
Before running DefaultDemo, predict the four lines it prints, one for each field.
- 1Check count
The declaration int count; has no initializer. The type is numeric, so the default is 0.
- 2Check flag
The declaration boolean flag; has no initializer. The type is boolean, so the default is false.
- 3Check letter
The declaration char letter; has no initializer. The default is the character with code number zero. Casting it to int prints 0.
- 4Check name
The declaration String name; has no initializer. String is an object type, so the default is null.
The program prints 0, false, 0, and null, in that order.
The 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. In DefaultDemo nothing assigns a value after creation, so the printed lines show the starting state alone.
Static Initialization Happens Once
Everything above concerns instance variables, which belong to each object. Static member variables follow a different pattern. There is only one copy of a static variable, so its initialization is executed just once, when the class is first loaded. Instance variable initialization repeats with every object, while static initialization happens once for the class as a whole.
Mistakes With Starting Values
Common mistake Assuming a String field with no initializer is an empty string
What it looks likeString name; followed by name.length() when no value has been assignedWhy it failsA String is an object type, so its default is null, not an empty string.
Do this insteadAssign a value explicitly, such as name = "", if an empty string is what the program needs.
Common mistake Assuming a field with no initializer holds some unpredictable value
What it looks likeint count; with no initializer, expecting the printed result to be a leftover numberWhy it failsJava supplies a defined default for every instance variable with no initializer, and for int that default is 0.
Do this insteadCheck the declaration for an initializer and the type's default rule before looking for other causes.
Common mistake Blaming the initializer when a later assignment changed the value
What it looks likedie1 = 3 in the declaration, but roll() has already changed die1 before the program prints itWhy it failsThe initializer sets the starting value only. Assignments made later, inside methods, change the value afterward.
Do this insteadAsk which moment produced the value: object creation, or a later assignment. Trace the assignments that ran before the read.
Locating an Unexpected Starting Value
A class declares public int die1 = 3; and public int die2; with no initializer. A program creates a new object and immediately prints die2. Without running the code, what value is printed, and which declaration explains it? Then explain what you would check first if the printed value were not what you predicted.
Hints
- Check each field declaration for an initializer after the equals sign.
- A numeric field with no initializer starts at the default for its type.
- If a field has an initializer but still shows a different value, look for an assignment that ran before the read.
Key Takeaways
- An explicit initializer, such as public int die1 = 3;, sets the starting value that each new object receives in that field.
- When a declaration has no initializer, Java supplies a default: zero for numeric types, false for boolean, the zero-code character for char, and null for object types including String.
- Instance variable initialization runs for every new object, so each object gets its own values, while static variables are initialized once when the class is first loaded.
- When a starting value looks wrong, check whether the declaration has an initializer, then check whether later code assigned a different value before the value was read.