Two kinds of attribute: per-object and per-class
In Topic 3.1 you met the idea that an attribute can be unique to each object (an instance variable) or shared by every object of the class (a class variable). Now you declare and use both.
Think of a school. Each student has their own locker number. That is per-student data: an instance variable. But the school also has a single count of how many students have enrolled. That number is not owned by any one student. It belongs to the school as a whole: a class variable.
Class variables
Class variables belong to the class, with all objects of a class sharing a single copy of the class variable. A class variable is designated with the keyword static before the variable type:
private static int nextId = 1; // one copy, shared
private int id; // a separate copy inside every object
If the class variable is public, code outside the class reaches it with the class name and the dot operator, because it is associated with the class, not with objects of the class: Member.MAX_BORROW.
A variable declared final cannot be modified once its value has been set. A constant is normally declared public static final: shared, readable everywhere and unchangeable.
Class methods
A class method is declared with static and belongs to the class too. You already use class methods such as Math.sqrt and Integer.parseInt. They are called with the class name.
There are firm rules about what a class method can reach:
- It can access or change the values of class variables and call other class methods.
- It cannot access or change the values of instance variables, or call instance methods, unless it is passed an instance of the class through a parameter.
The reason is that a class method runs without any particular object. Writing id in it would raise the question “whose id?”, and Java refuses. A class method that is given a Member as a parameter, on the other hand, has a particular object to work with.
Here is a class that uses all of these ideas:
public class Member
{
public static final int MAX_BORROW = 4; // constant: shared and cannot change
private static int nextId = 1; // class variable: one copy for all Members
private int id; // instance variable: one per Member
private String name;
public Member(String memberName)
{
name = memberName;
id = nextId;
nextId++;
}
public int getId()
{
return id;
}
public static int getCount()
{
return nextId - 1;
}
public static boolean sameId(Member a, Member b)
{
return a.id == b.id; // allowed: given instances, inside the class
}
}
nextIdis aprivate staticclass variable: one copy for allMembers. The constructor reads it to give each new member a unique identifier and then increments it.idis an instance variable: each member keeps its own.getCount()is a class method that uses only the class variable, so it is allowed.sameId(Member a, Member b)is a class method that reads instance variables of the objects it is given, which is allowed because they are passed in and the code is insideMember.MAX_BORROWis apublic static finalconstant, read from outside asMember.MAX_BORROW.
Worked example 1: unique identifiers
Task. Predict the output of this code using the Member class.
System.out.println(Member.getCount());
Member a = new Member("Ana");
Member b = new Member("Bo");
Member c = new Member("Cy");
System.out.println(a.getId() + " " + b.getId() + " " + c.getId());
System.out.println(Member.getCount());
System.out.println(Member.sameId(a, b) + " " + Member.sameId(c, c));
System.out.println(Member.MAX_BORROW);
Step 1. Before any object exists, nextId is 1 (the initial value of the class variable). Member.getCount() returns nextId - 1, which is 0. Notice that this call works with no object at all, because it is a class method.
Step 2. Trace the three constructors.
| Statement | id given to the new object |
nextId after |
|---|---|---|
| (before any object) | 1 | |
new Member("Ana") |
1 | 2 |
new Member("Bo") |
2 | 3 |
new Member("Cy") |
3 | 4 |
Step 3. Second print: 1 2 3.
Step 4. Third print: Member.getCount() is 4 - 1, which is 3.
Step 5. sameId(a, b) compares 1 and 2: false. sameId(c, c) compares 3 with 3: true. Fourth print: false true.
Step 6. Member.MAX_BORROW is 4.
The compiled program printed:
0
1 2 3
3
false true
4
Check. Every object got a different id, although the same line in the constructor ran each time, because nextId is shared and moves on after each object.
Worked example 2: shared versus per-object counts, and what the compiler rejects
Part A: a class variable and an instance variable side by side.
public class Tally
{
private static int all = 0;
private int mine = 0;
public void hit()
{
mine++;
all++;
}
public int getMine()
{
return mine;
}
public static int getAll()
{
return all;
}
public static void reset()
{
all = 0;
}
}
Tally p = new Tally();
Tally q = new Tally();
p.hit();
p.hit();
q.hit();
Tally.reset();
q.hit();
System.out.println(p.getMine() + " " + q.getMine() + " " + Tally.getAll());
| Statement | p.mine |
q.mine |
all (shared) |
|---|---|---|---|
| after both constructors | 0 | 0 | 0 |
p.hit() twice |
2 | 0 | 2 |
q.hit() |
2 | 1 | 3 |
Tally.reset() |
2 | 1 | 0 |
q.hit() |
2 | 2 | 1 |
The output was 2 2 1. The reset affected only the shared count, and the per-object counts continued.
Part B: a constant.
public class Shop
{
public static final double TAX_RATE = 0.25;
public static double withTax(double price)
{
return price * (1 + TAX_RATE);
}
}
Shop.withTax(80.0) evaluates 80.0 * (1 + 0.25), which is 100.0. The constant is read with the class name, Shop.TAX_RATE, which is 0.25. Both values were confirmed by running the code.
Part C: what the compiler rejects. This class has three mistakes.
public class Bad
{
private int id = 5;
private static int total = 0;
public static final int LIMIT = 10;
public int getId() { return id; }
public static int first() { return id; }
public static int second() { return getId(); }
public static int third() { LIMIT = 20; return total; }
}
The compiler reports:
first:non-static variable id cannot be referenced from a static context. A class method cannot read an instance variable without an instance.second:non-static method getId() cannot be referenced from a static context. A class method cannot call an instance method without an instance.third:cannot assign a value to static final variable LIMIT. A final variable cannot be changed. (Usingtotalthere is fine, because it is a class variable.)
Check. Each fix keeps the rule in view: pass an instance (first(Bad b) returning b.id), make the method non-static, and remove the assignment to the constant.
Common misconceptions
- “Static means the value can never change.” That is
final. Astaticvariable such asnextIdchanges freely; it is just shared. - “Each object has its own copy of a static variable.” There is one copy for the whole class.
- “A class method can use any instance variable of its class.” Only those of an object it has been given.
- “I need an object to call a class method.” You do not:
Member.getCount()works before anyMemberexists. - “
finalandprivatedo the same thing.”privatelimits who can use a variable;finallimits whether it can be changed. - “A constructor should reset the class variable.” Doing so would erase the shared count each time an object is built; the constructor should only update it.
Where this leads
Class variables for counting and identification are a staple of class-design problems. The next topic, Scope and Access, looks at where each kind of variable can be used. Work on the practice questions, read the revision notes and tick the topic checklist. If you need the instance-method rules again, see Methods: How to Write Them.
Quick questions
Why can't a static method use an instance variable?
A class method belongs to the class, not to any one object, so there is no particular object whose instance variable it could mean. It can use instance variables only of an object it is given, such as a parameter.
Do I call a static method on an object or on the class?
On the class, for example Member.getCount(). That is the clearest form and is what the course expects for public class variables and methods.
What does final do?
A variable declared final cannot be given a new value after it has been set. A constant in this course is typically declared public static final, so one shared, unchangeable value can be read from anywhere.
Does each object get its own copy of a static variable?
No. There is exactly one copy, shared by every object of the class. Changing it through one object changes what every other object sees.