Concepts / Static Nested Classes in Java

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.

  • Programming

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?

  • 0

  • 1

  • 2

  • 3

Reveal answer
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.

Code walkthrough
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));
    }
}
  1. 1
    Two Counter objects are created, a with label A and b with label B. Each starts with its own count of 0.
  2. 2
    a.new Incrementer() creates an inner object attached to a. ia is that inner object.
  3. 3
    b.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. 4
    ia.step() runs count++ on a, so a's count becomes 1. The line prints A: 1.
  5. 5
    The second ia.step() raises a's count to 2 and prints A: 2.
  6. 6
    ib.step() raises b's count to 1 and prints B: 1. a's count is unaffected.
  7. 7
    new 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
Output
A: 1
A: 2
B: 1
10
Worked example

Following 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?

  1. 1
    Start

    Object a has count 0 and object b has count 0. Both values belong to their own outer objects.

  2. 2
    First ia.step()

    ia is attached to a, so count++ changes a's count from 0 to 1. b's count is still 0.

  3. 3
    Second ia.step()

    a's count goes from 1 to 2 again. Still only a's field is changed.

  4. 4
    ib.step()

    ib is attached to b, so b's count goes from 0 to 1. a keeps its value of 2.

Result

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.

a.count2 after two stepsia.step()attached to ab.count1 after one stepib.step()attached to bincrementsincrements
When ia.step() runs, which outer object's count field changes, and which one stays the same?

Why Inner Means Attached to an Object

Definition

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.

Counter Acount and labelIncrementer for Aone copy per outer objectCounter Bcount and labelIncrementer for Bone copy per outer objectownsowns
Each inner object is tied to one outer object, even though both inner objects come from the same class definition.

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.

Key point

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.

Needs an instancevariable or methodof the outer class?Make it non-static(inner class)attached to an outer objectMake it staticno link to an outer objectyesno
Given what a nested class needs from its container, which path leads to static and which leads to non-static?

Mistakes With Inner Class Objects

  • Common mistake

    Treating an inner class as one shared object for the whole outer class

    What it looks like

    Assuming ia.step() and ib.step() both change the same counter because they come from the same Incrementer class.

    Why it fails

    Each inner object is attached to one outer object, so it works with that object's fields only.

    Do this instead

    Ask 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 like

    Writing new Incrementer() directly inside a static main method, with no outer object in front of it.

    Why it fails

    A non-static inner object needs an outer object it belongs to, and the code above does not supply one.

    Do this instead

    Create 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 like

    Declaring static class Incrementer with a step method that does count++ on the outer count field.

    Why it fails

    A static nested class has no link to an outer object, so it has no route to the outer instance data it needs.

    Do this instead

    Make 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 fails

    The link to an outer object adds nothing if the nested class never uses it.

    Do this instead

    Choose static when the nested class does not use any instance variable or instance method of the container.

Practice Before the Summary

EASY

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.