5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes

Key Takeaways

  • Java defines four nested class varieties: static nested classes, member inner classes, local inner classes, and anonymous inner classes, each with distinct scoping, lifecycle, and enclosing instance requirements.

  • Static nested classes behave like top-level classes in an outer namespace and require no outer instance, whereas member inner classes are tied to an enclosing instance and use outerRef.new Inner() syntax.

  • Java 16 relaxed legacy restrictions, permitting all inner class forms (member inner, local, and anonymous) to declare static members, including static fields, methods, records, and interfaces.

  • Local and anonymous classes can only access local variables and parameters from their enclosing method or block if those variables are final or effectively final (never modified after initialization).

  • Top-level and nested classes form a nest (JEP 181), enjoying bidirectional access to each other's private members without synthetic bridge methods.

Last updated: October 2026

Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes

Java allows developers to declare a class within another enclosing class or code block. These are known as nested classes. Grouping related classes within their logical context increases encapsulation, enhances maintainability, and prevents namespace pollution.

Java categorizes nested classes into two primary divisions:

  1. Static Nested Classes: Declared with the static modifier at the class member level.
  2. Inner Classes (Non-Static): Further divided into three types:
    • Member Inner Classes: Declared at the class member level without static.
    • Local Classes: Declared inside a method, constructor, or code block.
    • Anonymous Inner Classes: Declared and instantiated inline within an expression without a name.

Architectural Comparison of the Four Nested Types

The following table summarizes the structural properties and constraints of each nested class type:

Nested Class TypeScope & DeclarationOuter Instance Reference?Allowed Access ModifiersCan Declare Static Members? (Java 16+)Explicit Constructors?
Static NestedEnclosing class member level (static)No (static context)public, protected, default, privateYesYes
Member InnerEnclosing class member level (non-static)Yes (Outer.this)public, protected, default, privateYesYes
Local InnerInside method, constructor, or blockYes (if enclosing context is non-static)None (package/block-private only)YesYes
AnonymousInline within expression (unnamed)Yes (if enclosing context is non-static)NoneYesNo (instance initializers only)

Static Nested Classes

A static nested class is essentially a top-level class that has been packaged inside the namespace of an enclosing class for organizational convenience. It does not hold an implicit reference to an instance of the outer class.

Instantiation Syntax

Because no outer object instance is required, a static nested class is instantiated directly using the outer class's name:

OuterClass.StaticNested nested = new OuterClass.StaticNested();

If imported statically or accessed from within OuterClass, the prefix can be omitted:

StaticNested nested = new StaticNested();

Access Boundaries

Static nested classes can directly access all static members (methods and fields) of the enclosing class, including private static members. However, they cannot directly access instance variables or instance methods of the enclosing class because there is no implicit this reference pointing to an outer object:

public class OuterClass {
    private static int staticData = 100;
    private int instanceData = 200;

    public static class StaticNested {
        public void printValues() {
            System.out.println(staticData); // VALID: accesses private static member
            // System.out.println(instanceData); // COMPILE ERROR: non-static field cannot be referenced from static context
            System.out.println(new OuterClass().instanceData); // VALID: accessed via explicit outer instance
        }
    }
}

Member Inner Classes

A member inner class is declared within the body of an enclosing class without the static modifier. Unlike static nested classes, every instance of a member inner class is bound to a specific instance of the enclosing class.

Instantiation Mechanics

To instantiate a member inner class, an enclosing instance of OuterClass must already exist. The instantiation syntax reflects this outer-object dependency:

// From outside the outer class or from a static method:
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();

// Or combined in a single statement:
OuterClass.InnerClass inner2 = new OuterClass().new InnerClass();

Attempting to instantiate a member inner class from a static method using new InnerClass() triggers a compile error: non-static variable this cannot be referenced from a static context. However, from within an instance method of OuterClass, the implicit this reference is automatically supplied:

public class OuterClass {
    public class InnerClass {}

    public void createInner() {
        InnerClass inner = new InnerClass(); // Valid: uses this.new InnerClass()
    }
}

Disambiguating References with Qualified this

When a member inner class declares a member or parameter with the same name as a member in the enclosing class, shadowing occurs. To reference the outer instance's member, use the qualified this syntax: OuterClass.this.memberName:

public class Ship {
    private String name = "Nautilus";

    public class Engine {
        private String name = "Diesel";

        public void printNames(String name) {
            System.out.println(name);            // Method parameter
            System.out.println(this.name);       // Inner class field ("Diesel")
            System.out.println(Ship.this.name);  // Outer class field ("Nautilus")
        }
    }
}

The Java 16 Relaxation for Static Members

Prior to Java 16, non-static inner classes were strictly forbidden from declaring static members, with the sole exception of compile-time constant expressions (static final primitive or String fields).

Beginning in Java 16 (JEP 395) and fully active in Java 17, this restriction was lifted. Inner classes can now declare static members, including static fields, static methods, static initializers, records, and interfaces:

public class Outer {
    public class Inner {
        static int globalCount = 0; // Legal in Java 16+!
        static void reset() { globalCount = 0; } // Legal in Java 16+!
        record Point(int x, int y) {} // Legal in Java 16+!
    }
}

Warning

Outdated Study Guide Trap: Static in Inner Classes Older prep books and Java 8/11 guides claim that inner classes can never declare static methods or non-constant static fields. On the Java 17 (1Z0-829) exam, remember that Java 16+ permits member inner classes, local classes, and anonymous classes to declare static members!


Local Inner Classes

A local class is declared directly inside a block of executable code, such as a method body, constructor, or for loop.

Scoping and Modifiers

  • Visibility: A local class is visible only within the block in which it is defined, starting from its declaration line downward.
  • Modifiers: Because they are local to a block, local classes cannot have access modifiers (public, protected, private). They also cannot be declared static. They can, however, be marked abstract or final.

Variable Capture and Effectively Final Constraints

A local class can read fields from the enclosing outer class (both instance and static, including private). However, when accessing local variables or parameters declared in the enclosing method, a strict rule applies: the local variable must be final or effectively final.

An effectively final variable is one whose initial value is never changed after assignment:

public void process(int multiplier) {
    int base = 10;
    int counter = 0;
    counter++; // counter is NOT effectively final because it is reassigned

    class Calculator {
        void execute() {
            System.out.println(base * multiplier); // VALID: both are effectively final
            // System.out.println(counter); // COMPILE ERROR: local variable referenced from inner class must be final or effectively final
        }
    }

    new Calculator().execute();
}

If a variable is reassigned anywhere in the enclosing method—even after the local class declaration—it loses its effectively final status and triggers a compilation failure.


Anonymous Inner Classes

An anonymous class is a local inner class without a name. It is defined and instantiated simultaneously in a single expression using the new operator.

Syntax and Characteristics

Comparator<String> comp = new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
};

Key architectural rules governing anonymous classes:

  1. No Constructors: Because constructors must bear the name of their class, anonymous classes cannot declare constructors. Any required instance initialization must occur via an instance initializer block ({ ... }).
  2. Single Extension/Implementation: An anonymous class can extend exactly one class OR implement exactly one interface. It cannot specify an extends or implements clause, nor can it implement multiple interfaces.
  3. Effectively Final Capture: Like local classes, anonymous classes can capture enclosing local variables only if they are final or effectively final.
  4. Interaction with var: When assigned to var, members declared uniquely within the anonymous class body remain accessible because the compiler infers the exact anonymous type:
var runner = new Object() {
    int count = 5;
    void log() { System.out.println("Count: " + count); }
};
runner.log(); // Compiles cleanly via non-denotable type inference!

Nestmates and Private Member Access (JEP 181)

Top-level classes and all nested types within their compilation unit form a nest. Under the Java nestmate architecture (introduced in Java 11 via JEP 181), an outer class and all its nested classes (static, member, local, and anonymous) share mutual access to each other's private members without compiler-generated synthetic bridge methods (access$000), ensuring complete and bidirectional encapsulation across the nest.

Test Your Knowledge

Given the following class definition:

class Company {
    private String name = "TechCorp";

    class Department {
        private String deptName;
        Department(String deptName) { this.deptName = deptName; }
        void printInfo() {
            System.out.println(Company.this.name + " - " + deptName);
        }
    }
}

Which of the following code snippets correctly instantiates a Department object from a main method located outside Company?

A

Department dept = Company.new Department("Engineering");

B

Company.Department dept = new Company.Department("Engineering");

C

Company.Department dept = new Company().new Department("Engineering");

D

Company.Department dept = new Company.Department(new Company(), "Engineering");

Test Your Knowledge

Consider the following code snippet:

public class CalculationDemo {
    public static void main(String[] args) {
        int multiplier = 2;
        int base = 5;

        class MathOp {
            int run() {
                return base * multiplier;
            }
        }

        multiplier = 3;

        MathOp op = new MathOp();
        System.out.println(op.run());
    }
}

What is the result of attempting to compile and run this code?

A

Compilation fails at multiplier = 3; because local variables accessed by inner classes are implicitly final

B

Compiles successfully and prints 10

C

Compiles successfully and prints 15

D

Compilation fails inside MathOp.run() because multiplier is not effectively final

Test Your Knowledge

Consider the following class compiled under Java SE 17:

public class OuterContainer {
    private static int staticVal = 10;
    private int instanceVal = 20;

    public static class StaticNested {
        public void show() {
            System.out.print(staticVal + " ");
            System.out.print(instanceVal); // Line X
        }
    }

    public class MemberInner {
        static int innerStatic = 50; // Line Y
        public void show() {
            System.out.print(OuterContainer.this.instanceVal + " " + innerStatic);
        }
    }
}

What is the outcome of compiling this file under Java SE 17?

A

Compilation fails at Line Y because member inner classes are prohibited from declaring static fields.

B

The file compiles cleanly because static nested classes can access all private members of the enclosing class.

C

Compilation fails at both Line X and Line Y.

D

Compilation fails at Line X because a static nested class cannot directly access instance members of the enclosing class.

Test Your Knowledge

What is the output of running the following Java program?

public class Window {
    private int size = 100;

    public class Panel {
        private int size = 20;

        public void printSizes(int size) {
            System.out.print(size + " ");
            System.out.print(this.size + " ");
            System.out.print(Window.this.size);
        }
    }

    public static void main(String[] args) {
        new Window().new Panel().printSizes(5);
    }
}
A

5 5 100

B

Compilation fails because Window.this cannot be resolved inside printSizes

C

5 20 100

D

20 20 100

Sections you finish are checked off in the contents.