Instance Variables and Object State
A static class member, such as playerCount, belongs to the class itself and is stored as part of the class representation in memory. There is only one copy, and it exists as long as the program runs.
A Puzzle About Player Data
Imagine a program that describes players in a game. Each player has a name and an age, which seem to describe one particular player. But the program may also need a number that describes the game as a whole, such as how many players exist. Both kinds of data can be declared inside the same class, so a reasonable question arises: when a value is declared, who owns it, the class or each object created from the class?
What do you think happens?
Suppose a class declares one static member and two non-static members, and the program then creates two objects from that class. How many copies of the static member exist?
Reveal answer
One copy. The static member belongs to the class itself and is stored once as part of the class representation in memory.
The non-static members are the ones that get a separate copy for each object. The static member is not duplicated per object. Keep this answer in mind as the rest of the article shows where each member lives.
The rest of this article traces a single class through a sequence of steps. You will follow where each member is stored, watch one shared value change as objects are created, and then predict where a member can be reached from. The key idea is the one stated in the source: the single word static decides who owns a member, and that ownership decides how many copies exist.
Tracing the Three Declarations
The article uses one class throughout. It declares three members. The first, playerCount, is static. The other two, name and age, are not static. Read the declarations first, before tracing anything, because every later question refers back to them.
- static int
playerCount: the static member; one shared value for the whole class - String name and int age: non-static members; each object gets its own copy
- The constructor is included only to show when
playerCountchanges; the source does not describe it
- 1Look at the declaration before the type. If the keyword static is present, the member is static.
- 2If the keyword is absent, as with name and age, the member is non-static.
- 3A static member is stored as part of the class itself, so it is reached through the class name.
- 4A non-static member is stored for each object, so it is reached through an object.
Read the figure as a structural map. The static playerCount appears once, attached to the class, and it does not appear inside either object. The name and age values appear inside each object and not on the class, because they are not part of the class itself. Two objects are drawn here, but the same arrangement holds for as many objects as the program creates.
The Class Owns the Static Member
A static class member, such as playerCount, belongs to the class itself and is stored as part of the class representation in memory. There is only one copy of it, and it exists for as long as the program runs. Its full name is PlayerData.playerCount. The class name is enough to reach it, and no object has to be created first.
Because a static member belongs to the class, every use of the class shares the same value. If one part of the program changes playerCount through the class name, every other part that reads playerCount sees the updated value. This is what the source means when it says a static value is shared by everything that uses the class.
A static method follows the same ownership rule. It is part of the class itself, which is why static methods are also called class methods. Static members are therefore described as class variables and class methods, in contrast with the per-object members discussed next.
Each Object Owns Its Own Instance Data
A non-static class member, such as name or age, belongs to each instance object. Every object created from the class gets its own copy of the non-static part. So player1 has its own name and age, and player2 has its own name and age, and changing one object's age leaves the other object's age untouched.
Non-static variables do not become part of the class. That is why there is no PlayerData.name and no PlayerData.age. You cannot reach these variables through the class name alone. They only exist once an object has been created, and they are then reached through that object, such as player1.name.
When you see a non-static member in code, ask which object it belongs to before reading its value. A reference such as player1.age answers a question about one object only. A reference such as PlayerData.playerCount answers a question about the class as a whole.
Watching playerCount Change Across Objects
Creating two players and following playerCount
Starting from the PlayerData class above, the program creates player1 with name Asha and age 20, then creates player2 with name Ravi and age 25. What value does playerCount hold after each creation, and where does each value live?
- 1Before any object exists
The class has its single static copy of
playerCount, which holds 0. No object exists yet, so no name or age exists either. - 2After player1 is created
The constructor creates an object with its own name (Asha) and age (20). The single shared
playerCountis incremented, so the class-level value becomes 1. - 3After player2 is created
A second object is created with its own name (Ravi) and age (25). These values do not replace player1's values. The single shared
playerCountis incremented again, so it becomes 2. - 4Reading the values
PlayerData.playerCount gives 2, which describes the whole class.
player1.namegives Asha andplayer2.namegives Ravi, because each object holds its own name.
playerCount is 2 and is one shared value. name and age exist twice, once inside each object.
The diagram shows only the single shared value. Both player1 and player2 are aware of the same playerCount, because it belongs to the class and not to either object. Each object's name and age, by contrast, remain separate and are not shown in this progression.
Class Name Versus Object Access
The ownership rule decides how a member may be referenced. A static member can be reached through the class name, while a non-static member is reached through an object. The table summarizes the difference for the three members in the running example.
| Member | Keyword | Belongs to | Reached through the class name? | Copies that exist |
|---|---|---|---|---|
| playerCount | static | The class itself | Yes, as PlayerData.playerCount | One |
| name | none | Each object | No, there is no PlayerData.name | One per object |
| age | none | Each object | No, there is no PlayerData.age | One per object |
Ownership and access for the three members of PlayerData
Mistakes When Reading Shared and Per-Object Values
Common mistake Assuming every member is copied into each object
What it looks likeBelieving that playerCount exists separately inside player1 and player2.
Why it failsA static member belongs to the class and has only one copy, which exists as long as the program runs.
Do this insteadLook for the keyword static. If it is present, the value is stored once on the class and shared by all users of the class.
Common mistake Trying to reach a non-static member through the class name
What it looks likeWriting PlayerData.name and expecting a player's name.
Why it failsNon-static variables do not become part of the class, so there is no PlayerData.name.
Do this insteadReach the value through an object, such as player1.name.
Common mistake Expecting a change to one object's non-static value to affect another object
What it looks likeChanging player1.age and then expecting player2.age to change as well.
Why it failsEach object has its own copy of the non-static part, so the other object is untouched.
Do this insteadTrace the change through the specific object that was modified.
Common mistake Forgetting that a static value persists while the program runs, so a value changed earlier is still present later
Many surprising results in code come from assuming one kind of storage when the code actually used the other. When a result is unexpected, a useful first question is whether the value belongs to the class or to an object.
Predicting Where a Member Lives
Using the PlayerData class from this article, decide for each of the following expressions whether it refers to the class or to an object, and state where the value is stored: PlayerData.playerCount, player1.name, player2.age, and PlayerData.name. Explain which of these would be a valid reference.
Hints
- Check the declaration of each member:
playerCounthas the keyword static, while name and age do not. - A static member can be reached through the class name, and a non-static member is reached through an object.
- PlayerData.name is invalid because name is not part of the class itself.
A program creates three PlayerData objects and then reads PlayerData.playerCount. Without running the program, state the value you expect and explain which storage location produced it.
Hints
- Each constructor call increments the single shared
playerCountin this example. - The value is one shared value, not three separate values.
Key Takeaways
- A static member, such as
playerCount, belongs to the class itself, is stored once as part of the class representation, and exists for as long as the program runs. - A non-static member, such as name or age, belongs to each instance object, and every object created from the class gets its own copy.
- Static members are reached through the class name, as in PlayerData.playerCount, while non-static members are reached through an object, as in
player1.name. - When a result is unexpected, first ask whether the value belongs to the class or to an object, because that answer predicts whether changes are shared or private.