00:00

Static Nested Class in Java – Syntax, Uses and Examples

A static nested class is a class declared inside another class with the static modifier. It belongs to the outer class, not to any outer object, so it can be created without an instance of the outer class and holds no hidden reference to one. In practice it behaves like a top-level class that has been placed inside another for organisation and access control.

Static nested classes are the most common kind of nested class. This tutorial covers their syntax and access rules and three classic uses: builders, private implementation classes, and the lazy holder singleton.

1. Syntax

Syntax
class Outer {
    [access modifier] static class Nested {
        // fields, constructors, methods, static members
    }
}

Outer.Nested n = new Outer.Nested();   // no Outer object required

2. Access Rules

StaticNestedAccess.java
public class StaticNestedAccess {
    private static String appName = "JavaByTechie";   // outer static field
    private String user = "asha";                      // outer instance field

    static class Helper {
        String describe(StaticNestedAccess outer) {
            // appName: accessible directly (static, even though private)
            // user:    needs an explicit outer object
            return appName + " user = " + outer.user;
        }
    }

    public static void main(String[] args) {
        Helper h = new Helper();                       // no outer object needed
        System.out.println(h.describe(new StaticNestedAccess()));
    }
}
Output
JavaByTechie user = asha
  • A static nested class can access the outer class's static members directly, even private ones.
  • To use outer instance members it needs an explicit reference to an outer object.
  • The outer class can access the nested class's private members too.
  • It can have any access modifier, be final or abstract, extend classes and implement interfaces.

3. Use 1: The Builder Pattern

A builder collects optional settings and then creates an immutable object. Nesting it as Pizza.Builder lets it call Pizza's private constructor:

BuilderPattern.java
public class BuilderPattern {
    static class Pizza {
        private final String size;
        private final boolean extraCheese;
        private final String[] toppings;

        private Pizza(Builder b) {                   // only the Builder can construct
            this.size = b.size;
            this.extraCheese = b.extraCheese;
            this.toppings = b.toppings;
        }

        // Static nested class: needs no Pizza object to exist
        static class Builder {
            private String size = "Medium";
            private boolean extraCheese;
            private String[] toppings = {};

            Builder size(String size)            { this.size = size; return this; }
            Builder extraCheese()                { this.extraCheese = true; return this; }
            Builder toppings(String... toppings) { this.toppings = toppings; return this; }
            Pizza build()                        { return new Pizza(this); }   // private constructor OK
        }

        public String toString() {
            return size + " pizza" + (extraCheese ? " + extra cheese" : "")
                    + (toppings.length == 0 ? "" : " with " + String.join(", ", toppings));
        }
    }

    public static void main(String[] args) {
        Pizza p = new Pizza.Builder().size("Large").extraCheese().toppings("paneer", "onion").build();
        System.out.println(p);
        System.out.println(new Pizza.Builder().build());
    }
}
Output
Large pizza + extra cheese with paneer, onion
Medium pizza

The JDK uses this naming style too, for example HttpRequest.Builder and Locale.Builder, and libraries such as Lombok generate it.

4. Use 2: Private Implementation Classes

Data structures use private static nested classes for nodes and entries. HashMap.Node and LinkedList.Node are examples from the JDK:

LinkedStack.java
public class LinkedStack<T> {
    // Private static nested class: an implementation detail, no outer reference needed
    private static class Node<T> {
        final T value;
        final Node<T> next;
        Node(T value, Node<T> next) { this.value = value; this.next = next; }
    }

    private Node<T> top;
    private int size;

    void push(T value) { top = new Node<>(value, top); size++; }
    T pop() {
        if (top == null) throw new IllegalStateException("empty stack");
        T v = top.value;
        top = top.next;
        size--;
        return v;
    }

    public static void main(String[] args) {
        LinkedStack<String> history = new LinkedStack<>();
        history.push("/home");
        history.push("/java");
        history.push("/java/static-nested-class");
        System.out.println("size = " + history.size);
        while (history.size > 0) System.out.println("back to " + history.pop());
    }
}
Output
size = 3
back to /java/static-nested-class
back to /java
back to /home

Because Node is static, each node stores only its value and next pointer, not an extra reference to the stack object, saving memory for every element.

5. Use 3: Lazy, Thread-Safe Singleton (Holder Idiom)

A nested class is loaded and initialised only when it is first used. The JVM guarantees that class initialisation is thread-safe, which gives lazy singletons without any locking:

HolderSingleton.java
public class HolderSingleton {
    static class Config {
        private Config() { System.out.println("Config created (lazily, thread-safe)"); }

        // The holder class is loaded only when getInstance() is first called
        private static class Holder {
            static final Config INSTANCE = new Config();
        }
        static Config getInstance() { return Holder.INSTANCE; }
        static void ping() { System.out.println("Config.ping() - instance not created yet"); }
    }

    public static void main(String[] args) {
        Config.ping();
        Config a = Config.getInstance();
        Config b = Config.getInstance();
        System.out.println("same instance: " + (a == b));
    }
}
Output
Config.ping() - instance not created yet
Config created (lazily, thread-safe)
same instance: true

6. Static Nested Class vs Inner Class

AspectStatic nested classInner (non-static) class
KeywordstaticNone
Instantiationnew Outer.Nested()outer.new Inner()
Outer instance referenceNoneHidden reference in every object
Access to outer instance membersThrough an explicit reference onlyDirect
Access to outer static membersDirectDirect
Memory-leak riskLowCan keep the outer object alive

Effective Java's advice: "favour static member classes over non-static" unless each instance truly needs its enclosing instance.

7. Notes

  • The class file is Outer$Nested.class; with reflection, use Class.forName("pkg.Outer$Nested").
  • Static nested classes can be imported: import pkg.Outer.Nested;.
  • Nested interfaces, enums and records are always implicitly static.
  • A static nested class with a main method can be run with java Outer$Nested (quote the $ in Unix shells).

8. Summary

  • A static nested class is declared with static inside another class and needs no outer instance.
  • It accesses outer static members directly and instance members only through a reference.
  • Common uses: builders, private node/entry classes and the lazy holder singleton.
  • Prefer it over an inner class whenever the outer instance is not needed.

Related Tutorials