By WebNest Studio Engineering Team
Java - Core Tutorial
Nested and Inner Classes
Java allows a class to be defined inside another class. Doing this groups classes that are logically related and used in only one place, tightens encapsulation by hiding a helper class from the rest of the package, and, for certain kinds of nested classes, gives the inner class direct access to the enclosing object's fields and methods.
Java distinguishes several flavors of nested types — static nested classes, (non-static) inner classes, local classes, and anonymous classes — and each has a different relationship to the instance of the class that contains it.
Why it matters
Nested classes keep a helper type next to the only class that uses it, and anonymous classes were how Java passed behaviour around before lambdas — you still read them in older code every day. The key distinction is whether the nested class is static: a non-static inner class silently holds a reference to its outer object, which is a common cause of memory leaks.
Static Nested Classes vs Inner Classes
A static nested class is declared with the static keyword inside another class. It behaves like any top-level class that just happens to be namespaced inside another — it does not hold a reference to any particular instance of the outer class, and it cannot access the outer class's instance fields or methods directly. A (non-static) inner class has no static keyword, and every instance of it is implicitly tied to one specific instance of the outer class. This means an inner class instance always carries a hidden reference to its enclosing object, so it can freely read and modify the outer object's instance fields — but it also means you cannot create an inner class instance without first having (or creating) an outer class instance, using the syntax outerInstance.new InnerClass().
- Static nested class: created as
new Outer.Nested(); no implicit link to an outer instance. - Inner class: created as
outer.new Inner(); each instance is bound to one outer instance and can access its fields directly.
Local Classes
A local class is defined entirely inside a method body (or another block, like a for-loop or if-statement), and it is only visible within that block. Local classes are useful when a helper class is needed for a single, self-contained piece of logic and doesn't deserve to exist outside that method. Like inner classes, a local class defined inside an instance method can access the enclosing instance's fields, and it can also access effectively final local variables from the enclosing method (variables that are never reassigned after initialization).
Anonymous Classes
An anonymous class is a local class without a name, declared and instantiated in a single expression, typically to provide a one-off implementation of an interface or an extension of a class. Before Java 8 introduced lambda expressions, anonymous classes were the standard way to implement callback-style interfaces like Runnable or Comparator inline. They are still useful today when the interface has more than one abstract method (so a lambda cannot represent it), or when the implementation needs its own additional fields or initialization logic.
When Each Is Appropriate
Choose a static nested class when the nested type is a logical helper that doesn't need access to an outer instance (for example, a Builder class, or a small data-holder type used only by the outer class). Choose an inner class when instances of the nested type are conceptually always tied to one specific outer instance (for example, an Iterator implementation tied to one particular collection instance). Choose a local or anonymous class for small, throwaway implementations needed in exactly one place — though in modern Java, a lambda expression is usually preferred over an anonymous class whenever the target is a single-abstract-method (functional) interface.
Examples
A static nested class versus a non-static inner class
public class Outer {
private int outerField = 10;
static class StaticNested {
void display() {
System.out.println("Static nested class has no link to any Outer instance");
}
}
class Inner {
void display() {
System.out.println("Inner class reads outerField = " + outerField);
}
}
public static void main(String[] args) {
Outer.StaticNested nested = new Outer.StaticNested();
nested.display();
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();
}
}
Static nested class has no link to any Outer instance
Inner class reads outerField = 10
Anonymous classes implementing Comparator and Runnable
import java.util.*;
public class AnonymousClassDemo {
public static void main(String[] args) {
List<String> names = new ArrayList<>(Arrays.asList("Banana", "apple", "Cherry"));
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareToIgnoreCase(b);
}
});
System.out.println(names);
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Running from an anonymous Runnable");
}
};
task.run();
}
}
[apple, Banana, Cherry]
Running from an anonymous Runnable
Common Mistakes
- Trying to create an inner class instance with plain "new Inner()" from a static context — a non-static inner class requires an existing outer instance: "outer.new Inner()".
- Giving a static nested class access to instance fields of the outer class and being surprised by a compile error — static nested classes have no implicit outer instance to read from.
- Capturing a local variable in a local or anonymous class after reassigning it — such variables must be effectively final (never reassigned) to be captured.
- Reaching for an anonymous class out of habit when a lambda expression would be shorter and clearer for a single-abstract-method interface like Runnable or Comparator.
Exercise
Sort the names by length, shortest first, by passing an anonymous class that implements Comparator<String> to Arrays.sort.
Expected output: [Al, Ravi, Meera]
import java.util.Arrays;
import java.util.Comparator;
public class SortByLength {
public static void main(String[] args) {
String[] names = {"Meera", "Al", "Ravi"};
// TODO: sort names with an anonymous Comparator that compares lengths
System.out.println(Arrays.toString(names));
}
}
Show hints
- An anonymous class is written as
new Comparator<String>() { ... }with the method inside the braces. Integer.compare(a.length(), b.length())returns the negative, zero or positive value a comparator needs.
Show solution
import java.util.Arrays;
import java.util.Comparator;
public class SortByLength {
public static void main(String[] args) {
String[] names = {"Meera", "Al", "Ravi"};
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
});
System.out.println(Arrays.toString(names)); // [Al, Ravi, Meera]
}
}
Quick quiz
1. How do you create an instance of a non-static inner class Inner from outside its outer class?
- A. outerObject.new Inner()
- B. new Inner()
- C. Outer.new Inner()
- D. new Outer.Inner() without an outer object
Show answer
A. An inner class instance is tied to an outer object, so it is created through one: outerObject.new Inner().
2. Can a static nested class use the instance fields of its outer class directly?
- A. Yes
- B. No, it has no outer object; it needs an explicit reference
- C. Only private fields
- D. Only final fields
Show answer
B. A static nested class is not attached to an outer instance, so it can only reach instance members through an object you give it.
3. Can an anonymous class declare a constructor?
- A. Yes
- B. Only a no-argument constructor
- C. No, because it has no name
- D. Only if it extends a class
Show answer
C. A constructor must be named after its class, and an anonymous class has no name. It can use an instance initializer block instead.
Interview questions
What are the types of nested class in Java?
Show answer
Four. A static nested class is declared static inside another class and does not need an outer object. An inner (member) class is non-static and belongs to an instance of the outer class. A local class is declared inside a method. An anonymous class is declared and instantiated in one expression, with no name.
Why is a static nested class usually preferred over an inner class?
Show answer
An inner class instance keeps a hidden reference to the outer object that created it. If the inner object lives longer than expected — stored in a collection, or passed to another thread — it keeps the outer object from being garbage collected. A static nested class has no such reference, so use it unless the nested class really needs the outer object's state.
Key Points to Remember
- Static nested classes behave like independent top-level classes with no link to an outer instance; inner classes are bound to one specific outer instance and can access its fields directly.
- Local classes live inside a method body and can access effectively final local variables plus the enclosing instance's members.
- Anonymous classes are unnamed, one-off local classes, historically used for inline interface implementations before lambdas existed.
- Prefer lambdas over anonymous classes for functional interfaces; reserve anonymous/local classes for multi-method interfaces or cases needing extra state.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering