Static Nested Classes in Java
A non-static nested class is called an inner class, and it is associated with an object of the containing class rather than with the containing class itself.
A Puzzle With Two Counters
Java lets you define one class inside the body of another. The class written inside is called a nested class. Nested classes come in two forms, static and non-static, and the non-static form has its own name: inner class. This article focuses on the non-static form, because it behaves differently from what most beginners expect. A non-static nested class is not simply a class that happens to sit inside another class. It is tied to objects of the surrounding class, and that tie determines what the inner class can see and what it cannot.
Imagine a Counter class that has two objects, A and B. Each Counter object has its own inner class, and the inner class increments that particular object's counter. Both inner objects come from the same class definition. Before reading the code in the next section, make a prediction: when the second inner object is used, does it change the counter belonging to A, the counter belonging to B, or both?
What do you think happens?
After two steps on A's inner object and one step on B's inner object, what is the value of B's counter?
Reveal answer
1
Each outer object has its own counter. B's inner object only changes the counter attached to B, which starts at 0 and goes up by one. A's two steps do not touch it.
Tracing the Counter Program
The program below is an original illustration written to make the association visible. The outer class Counter has a private counter and a label. Its inner class Incrementer increments the counter and prints it. A separate static nested class, Helper, is included for comparison, and it does not touch any outer instance data. Read the code first and think about what each outer object's counter looks like after the calls.
public class Counter {
private int count = 0;
private String label;
public Counter(String label) {
this.label = label;
}
class Incrementer {
void step() {
count++;
System.out.println(label + ": " + count);
}
}
static class Helper {
int doubleIt(int x) {
return x * 2;
}
}
public static void main(String[] args) {
Counter a = new Counter("A");
Counter b = new Counter("B");
Counter.Incrementer ia = a.new Incrementer();
Counter.Incrementer ib = b.new Incrementer();
ia.step();
ia.step();
ib.step();
System.out.println(new Counter.Helper().doubleIt(5));
}
}- 1Two Counter objects are created, a with label A and b with label B. Each starts with its own count of 0.
- 2a.new
Incrementer()creates an inner object attached to a. ia is that inner object. - 3b.new
Incrementer()creates a second inner object attached to b. ib is that inner object. It comes from the same class definition as ia, but it is attached to a different outer object. - 4
ia.step()runs count++ on a, so a's count becomes 1. The line prints A: 1. - 5The second
ia.step()raises a's count to 2 and prints A: 2. - 6
ib.step()raises b's count to 1 and prints B: 1. a's count is unaffected. - 7new
Counter.Helper().doubleIt(5) does not depend on any Counter object, so it prints 10.
- count and label are private instance variables of the outer Counter object
- Incrementer is non-static, so every instance of it is attached to one Counter object
- Helper is static and never reads count or label
- a.new
Incrementer()creates an inner object attached to the object a
A: 1
A: 2
B: 1
10Following the counter values through the calls
After the lines ia.step(), ia.step(), and ib.step() run, what is the state of each outer object's count?
- 1Start
Object a has count 0 and object b has count 0. Both values belong to their own outer objects.
- 2First ia.step()
ia is attached to a, so count++ changes a's count from 0 to 1. b's count is still 0.
- 3Second ia.step()
a's count goes from 1 to 2 again. Still only a's field is changed.
- 4ib.step()
ib is attached to b, so b's count goes from 0 to 1. a keeps its value of 2.
a has count 2 and b has count 1. The two inner objects came from the same class, but each one worked on the data of its own outer object.
Why Inner Means Attached to an Object
A non-static nested class is called an inner class, and it is associated with an object of the containing class rather than with the containing class itself.
The word inner captures the relationship. An inner class is not a separate piece of the program that exists once for the whole outer class. Its existence is tied to an outer object. The source explains that the right way to picture it is not one inner class for the outer class, but an inner class attached to each outer object. That single shift in thinking is what the rest of the topic builds on.
A useful comparison is with ordinary non-static members. A non-static field or method has its source code written inside the class definition, but what it describes is the state and behavior of each object created from that class. An inner class follows the same logic. Its source sits inside the containing class, yet it belongs to the object that owns it.
The source describes each outer object as logically carrying its own copy of the nested class. This is a logical description, not a literal one. An outer object does not physically hold a second copy of the compiled code. What matters is behavior: each copy works with the instance variables and instance methods of the particular outer object it belongs to. This is why two inner objects can look identical in source code and still behave differently when they run.
How Inner Classes Reach Private Outer Data
Because each inner copy is attached to its outer object, it can use all of that object's instance methods and instance variables. This access extends to members declared private. An inner class is therefore not restricted in the way an unrelated class would be if it tried to read another class's private data.
In the counter program, Incrementer's step method reads and writes count and label directly, even though both are private fields of Counter. The reach is always to the outer object the inner object is attached to. When ia.step() runs, count refers to a's count. It does not refer to b's count, and that is why each call changes only the outer object whose inner object was used.
When an inner class needs a value that belongs to a particular outer object, that value comes from the outer object the inner object is attached to. Keep this question in mind when reading any inner class: which outer object does this inner object belong to?
Deciding Between Static and Non-Static
Once you know that a non-static nested class is tied to an outer object, the choice between static and non-static becomes a practical decision. The source gives a simple rule: if the nested class needs to use any instance variable or instance method from the containing class, make it non-static. Otherwise, it might as well be static.
The reasoning follows from the association. A non-static nested class has an outer object behind it, so it can reach that object's instance data. A static nested class has no such link. If it needs instance data from the containing class, it has no route to that data, so it must be non-static. If it does not need that data, the link adds nothing, and a static nested class is the natural choice. The Helper class in the counter program is an example of the second case: it only multiplies its argument and never reads any Counter field.
Mistakes With Inner Class Objects
Common mistake Treating an inner class as one shared object for the whole outer class
What it looks likeAssuming ia.step() and ib.step() both change the same counter because they come from the same Incrementer class.
Why it failsEach inner object is attached to one outer object, so it works with that object's fields only.
Do this insteadAsk which outer object an inner object was created from, and expect changes to affect only that object.
Common mistake Creating an inner object without an outer object to attach it to
What it looks likeWriting new Incrementer() directly inside a static main method, with no outer object in front of it.
Why it failsA non-static inner object needs an outer object it belongs to, and the code above does not supply one.
Do this insteadCreate the outer object first and then create the inner object from it, as in a.new Incrementer().
Common mistake Marking a nested class static when it reads an outer instance variable
What it looks likeDeclaring static class Incrementer with a step method that does count++ on the outer count field.
Why it failsA static nested class has no link to an outer object, so it has no route to the outer instance data it needs.
Do this insteadMake the nested class non-static whenever it uses any instance variable or instance method of the containing class.
Common mistake Making a nested class non-static when it does not need outer data
Why it failsThe link to an outer object adds nothing if the nested class never uses it.
Do this insteadChoose static when the nested class does not use any instance variable or instance method of the container.
Practice Before the Summary
Using the Counter class from this article, suppose you add a third object c = new Counter("C") with its own inner object ic. Then you call ic.step() once, ia.step() once, and ia.step() once more. What does each of these calls print, and what is the final count of each outer object? Explain which outer object each call changes.
Hints
- Each outer object starts with its own count of 0.
- ic is attached to c, so only c's count changes when
ic.step()runs. - Trace ia and ic separately, the same way the worked example did.
At a glance
- A non-static nested class is called an inner class, and it is associated with an object of the containing class rather than with the containing class itself.
- Each outer object logically carries its own copy of the inner class, and each copy works with the instance variables and instance methods of the outer object it belongs to.
- An inner class can use its outer object's instance methods and instance variables, including private ones, so two inner objects attached to different outer objects work with different data.
- Choose non-static when the nested class uses any instance variable or instance method of the containing class; otherwise it can be static.
- Common mistakes include assuming all inner objects share one counter, creating inner objects without an outer object, and marking a class static when it needs outer instance data.