Java Inner Classes
Run each complete program separately. Its required helper classes and imports are included in the same code block. Short syntax fragments explain individual operations.
A class can be declared inside another class or within a method. Its location and modifiers decide whether it needs an outer object and which surrounding values it can use. We will compare member, local, anonymous, and static nested classes through those rules.
Inner classes allow us to logically group classes that are closely related. They can also access members of their enclosing class, including private members.
For example:
class Outer {
private int number = 100;
class Inner {
void display() {
System.out.println(number);
}
}
}Here, Inner is declared inside Outer, so Inner is an inner class.
What Is an Inner Class?
A class declared inside another class is called a nested class.
When a nested class is not declared static, it is specifically called a non-static inner class.
Example
class Outer {
class Inner {
}
}Here:
Outer
└── InnerInner is a non-static inner class.
Java supports several forms of nested classes:
- Member inner class
- Static nested class
- Local inner class
- Anonymous inner class
Types of Nested and Inner Classes
Java's nested-class structure can be understood as:
Nested Classes
│
├── Static Nested Class
│
└── Inner Classes
│
├── Member Inner Class
├── Local Inner Class
└── Anonymous Inner ClassThe important distinction is:
A non-static nested class is called an inner class.
1. Member Inner Class
A member inner class is a non-static class declared directly inside another class.
Example
class Outer {
private int number = 100;
class Inner {
void display() {
System.out.println(number);
}
}
}Here, Inner is a member inner class.
Creating an Object of a Member Inner Class
A non-static inner class is associated with an instance of its outer class.
Example
class Outer {
class Inner {
void display() {
System.out.println("Inside Inner class");
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();
}
}Output
Inside Inner classNotice the syntax:
Outer.Inner inner = outer.new Inner();The Inner object is created using an instance of Outer.
Why Is outer.new Inner() Required?
A non-static inner class is associated with an instance of its outer class.
Therefore:
Outer outer = new Outer();creates the outer object.
Then:
Outer.Inner inner = outer.new Inner();creates an inner object associated with that outer object.
Conceptually:
Outer Object
│
└── Inner ObjectInner Class Accessing Outer Class Members
An inner class can directly access members of its enclosing class.
Example
class Outer {
private String message = "Hello from Outer";
class Inner {
void display() {
System.out.println(message);
}
}
}Usage:
class Outer {
private String message = "Hello from Outer";
class Inner {
void display() {
System.out.println(message);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();
}
}Output
Hello from OuterThe message field is private, but the inner class can access it.
Inner Class Accessing Private Members
Inner classes have access to members of their enclosing class, including private members.
Example
class Employee {
private String name = "John";
class Details {
void print() {
System.out.println(name);
}
}
}Usage:
class Employee {
private String name = "John";
class Details {
void print() {
System.out.println(name);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Employee employee = new Employee();
Employee.Details details =
employee.new Details();
details.print();
}
}Output
JohnOuter Class Accessing Inner Class Members
The outer class can access members of its inner class through an inner-class object.
Example
class Outer {
class Inner {
private int number = 100;
}
void display() {
Inner inner = new Inner();
System.out.println(inner.number);
}
}Usage:
class Outer {
class Inner {
private int number = 100;
}
void display() {
Inner inner = new Inner();
System.out.println(inner.number);
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
outer.display();
}
}Output
100The outer class can access the private member because the two classes are closely related and the Java language provides the required access.
Accessing Outer Class Members When Names Conflict
Sometimes an inner class and outer class have fields with the same name.
Example
class Outer {
int number = 10;
class Inner {
int number = 20;
void display() {
System.out.println(number);
System.out.println(this.number);
System.out.println(Outer.this.number);
}
}
}Usage:
class Outer {
int number = 10;
class Inner {
int number = 20;
void display() {
System.out.println(number);
System.out.println(this.number);
System.out.println(Outer.this.number);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display();
}
}Output
20
20
10Explanation
numberrefers to the nearest variable in scope.
this.numberrefers to the current inner-class object's field.
Outer.this.numberrefers to the field of the enclosing Outer object.
Outer.this
Outer.this is used to explicitly refer to the current object of the outer class.
Example
class Outer {
String name = "Outer";
class Inner {
String name = "Inner";
void display() {
System.out.println(name);
System.out.println(this.name);
System.out.println(Outer.this.name);
}
}
}Output:
Inner
Inner
OuterThis is particularly useful when both classes contain fields with the same name.
Multiple Inner Class Objects
Multiple inner objects can be associated with the same outer object.
Example
class Outer {
class Inner {
void display() {
System.out.println("Inner object");
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner first = outer.new Inner();
Outer.Inner second = outer.new Inner();
first.display();
second.display();
}
}Output
Inner object
Inner objectBoth inner objects are associated with the same outer instance.
Multiple Outer Objects
Different outer objects can have their own inner objects.
Example
class Outer {
private int value;
Outer(int value) {
this.value = value;
}
class Inner {
void display() {
System.out.println(value);
}
}
}Usage:
class Outer {
private int value;
Outer(int value) {
this.value = value;
}
class Inner {
void display() {
System.out.println(value);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer firstOuter = new Outer(100);
Outer secondOuter = new Outer(200);
Outer.Inner firstInner =
firstOuter.new Inner();
Outer.Inner secondInner =
secondOuter.new Inner();
firstInner.display();
secondInner.display();
}
}Output
100
200Each inner object is associated with its particular outer object.
2. Static Nested Class
A class declared with static inside another class is called a static nested class.
Example
class Outer {
static class Nested {
void display() {
System.out.println("Static nested class");
}
}
}Unlike a non-static inner class, a static nested class does not require an instance of the outer class.
Creating a Static Nested Class Object
class Outer {
static class Nested {
void display() {
System.out.println("Static nested class");
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer.Nested nested =
new Outer.Nested();
nested.display();
}
}Output
Static nested classNotice that we do not need:
Outer outer = new Outer();Static Nested Class vs Inner Class
Compare:
Inner class
Outer outer = new Outer();
Outer.Inner inner =
outer.new Inner();Static nested class
Outer.Nested nested =
new Outer.Nested();The key difference is the relationship with an outer object.
| Feature | Inner Class | Static Nested Class |
|---|---|---|
static | No | Yes |
| Requires outer object | Yes | No |
| Can directly access outer instance members | Yes | No |
| Can access outer static members | Yes | Yes |
| Object creation | outer.new Inner() | new Outer.Nested() |
Static Nested Class Accessing Outer Members
A static nested class can directly access static members of the outer class.
Example
class Outer {
static int number = 100;
static class Nested {
void display() {
System.out.println(number);
}
}
}Usage:
class Outer {
static int number = 100;
static class Nested {
void display() {
System.out.println(number);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer.Nested nested =
new Outer.Nested();
nested.display();
}
}Output
100Static Nested Class Cannot Directly Access Outer Instance Members
Consider:
class Outer {
int number = 100;
static class Nested {
void display() {
System.out.println(number);
}
}
}This does not compile because number belongs to an instance of Outer, but the static nested class does not automatically have an outer instance.
To access it, an Outer object must be explicitly provided.
Example
class Outer {
int number = 100;
static class Nested {
void display(Outer outer) {
System.out.println(outer.number);
}
}
}Usage:
Outer.Nested nested =
new Outer.Nested();
nested.display(new Outer());3. Local Inner Class
A local inner class is a class declared inside a method, constructor, or block.
Example
class Outer {
void display() {
class Local {
void print() {
System.out.println("Local inner class");
}
}
Local local = new Local();
local.print();
}
}Usage:
class Outer {
void display() {
class Local {
void print() {
System.out.println("Local inner class");
}
}
Local local = new Local();
local.print();
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
outer.display();
}
}Output
Local inner classThe Local class exists only within the scope where it is declared.
Local Inner Class Scope
Consider:
class Outer {
void method() {
class Local {
void display() {
System.out.println("Hello");
}
}
Local local = new Local();
local.display();
}
}Local is accessible inside method().
It cannot be directly referenced from another method:
void anotherMethod() {
Local local; // Not accessible here
}because the class is local to method().
Local Inner Class Accessing Local Variables
A local inner class can access local variables from the enclosing method if those variables are final or effectively final.
Example
class Outer {
void display() {
int number = 100;
class Local {
void print() {
System.out.println(number);
}
}
Local local = new Local();
local.print();
}
}Output
100number is effectively final because its value is not changed after initialization.
Effectively Final Variable
A variable is effectively final if it is not explicitly declared final but its value is never changed.
This is valid:
int number = 100;
class Local {
void display() {
System.out.println(number);
}
}This is not valid:
int number = 100;
number = 200;
class Local {
void display() {
System.out.println(number);
}
}The local variable is no longer effectively final.
You can explicitly use:
final int number = 100;to make the intention clear.
Local Inner Class Accessing Instance Members
A local inner class can access members of its enclosing object.
Example
class Employee {
private String name = "John";
void display() {
class Local {
void print() {
System.out.println(name);
}
}
Local local = new Local();
local.print();
}
}Output
John4. Anonymous Inner Class
An anonymous inner class is a class without a name that is declared and instantiated at the same time.
Example
abstract class Animal {
abstract void sound();
}We can create an anonymous implementation:
abstract class Animal {
abstract void sound();
}
public class HelloWorld {
public static void main(String[] args) {
Animal animal = new Animal() {
@Override
void sound() {
System.out.println("Dog barks");
}
};
animal.sound();
}
}Output
Dog barksThere is no class name for the implementation.
Anonymous Inner Class with Interface
Anonymous inner classes are commonly used with interfaces.
Example
interface Greeting {
void sayHello();
}Implementation:
interface Greeting {
void sayHello();
}
public class HelloWorld {
public static void main(String[] args) {
Greeting greeting = new Greeting() {
@Override
public void sayHello() {
System.out.println("Hello Java");
}
};
greeting.sayHello();
}
}Output
Hello JavaAnonymous Inner Class with a Normal Class
An anonymous inner class can also extend a normal class.
Example
class Animal {
void sound() {
System.out.println("Animal sound");
}
}Usage:
class Animal {
void sound() {
System.out.println("Animal sound");
}
}
public class HelloWorld {
public static void main(String[] args) {
Animal animal = new Animal() {
@Override
void sound() {
System.out.println("Dog barks");
}
};
animal.sound();
}
}Output
Dog barksAnonymous Inner Class with Additional Methods
Consider:
Animal animal = new Animal() {
@Override
void sound() {
System.out.println("Dog barks");
}
void run() {
System.out.println("Dog runs");
}
};The run() method belongs to the anonymous class.
However, it is not accessible through the variable declared as Animal:
animal.run();because the compile-time type of animal is Animal.
This is one reason anonymous classes are generally used for a small, focused implementation.
Anonymous Inner Class and Constructor Arguments
An anonymous class can call the constructor of its superclass.
Example
class Person {
Person(String name) {
System.out.println("Name: " + name);
}
}Usage:
Person person = new Person("John") {
void display() {
System.out.println("Anonymous class");
}
};The superclass constructor receives "John".
Anonymous Inner Class vs Lambda
For a functional interface:
interface Calculator {
int add(int a, int b);
}An anonymous class can be written as:
Calculator calculator = new Calculator() {
@Override
public int add(int a, int b) {
return a + b;
}
};For a functional interface, a lambda is often more concise:
Calculator calculator =
(a, b) -> a + b;Important
Lambdas are not simply a shorter syntax for every anonymous inner class.
A lambda is specifically used with a functional interface, while an anonymous class can extend a class or implement an interface and can contain its own fields and methods.
Inner Class Implementing an Interface
A named inner class can implement an interface.
Example
class Outer {
class Printer implements Runnable {
@Override
public void run() {
System.out.println(
"Running inner class"
);
}
}
}Usage:
class Outer {
class Printer implements Runnable {
@Override
public void run() {
System.out.println(
"Running inner class"
);
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Printer printer =
outer.new Printer();
printer.run();
}
}Output
Running inner classNested Classes Inside Interfaces
Interfaces can contain nested types.
Example
interface Outer {
class Helper {
void display() {
System.out.println("Helper");
}
}
}The nested class is implicitly static when declared as a member of an interface.
Usage:
interface Outer {
class Helper {
void display() {
System.out.println("Helper");
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer.Helper helper =
new Outer.Helper();
helper.display();
}
}Output
HelperInner Classes Inside Inner Classes
A nested class can itself contain another class.
Example
class Outer {
class Inner {
class DeepInner {
void display() {
System.out.println(
"Deep inner class"
);
}
}
}
}Objects can be created like this:
class Outer {
class Inner {
class DeepInner {
void display() {
System.out.println(
"Deep inner class"
);
}
}
}
}
public class HelloWorld {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner =
outer.new Inner();
Outer.Inner.DeepInner deep =
inner.new DeepInner();
deep.display();
}
}Output
Deep inner classAlthough possible, deeply nested structures can make code harder to understand, so they should be used only when they provide a clear design benefit.
Access Modifiers in Inner Classes
Member inner classes can use access modifiers such as:
public
protected
privateExample
class Outer {
private class Inner {
void display() {
System.out.println("Private inner class");
}
}
}A private inner class can be used only where its access rules permit.
Local and anonymous classes have different declaration rules because they are scoped within methods or expressions.
Inner Class and Inheritance
An inner class can extend another class.
Example
class Parent {
void display() {
System.out.println("Parent");
}
}
class Outer {
class Inner extends Parent {
@Override
void display() {
System.out.println("Inner");
}
}
}Usage:
Outer outer = new Outer();
Outer.Inner inner =
outer.new Inner();
inner.display();Output
InnerInner Class and Encapsulation
Inner classes can be useful for encapsulation.
For example, an outer class can hide implementation details inside a private nested class.
class DataService {
private class DataProcessor {
void process() {
System.out.println("Processing data");
}
}
public void execute() {
DataProcessor processor =
new DataProcessor();
processor.process();
}
}External code does not need to know about DataProcessor.
It only interacts with:
DataServiceThis can help keep implementation details hidden.
Practical Example: Iterator-Like Design
Suppose we have a simple collection class:
class NumberCollection {
private int[] numbers = {10, 20, 30};
class Printer {
void print() {
for (int number : numbers) {
System.out.println(number);
}
}
}
}Usage:
class NumberCollection {
private int[] numbers = {10, 20, 30};
class Printer {
void print() {
for (int number : numbers) {
System.out.println(number);
}
}
}
}
public class HelloWorld {
public static void main(String[] args) {
NumberCollection collection =
new NumberCollection();
NumberCollection.Printer printer =
collection.new Printer();
printer.print();
}
}Output
10
20
30The inner class has direct access to the outer class's private state.
Practical Example: Event Handling
Anonymous inner classes were historically common in GUI event handling.
Example pattern:
Button button = new Button();
button.setListener(new Listener() {
@Override
public void onClick() {
System.out.println("Button clicked");
}
});The anonymous class provides a small implementation specifically for that event.
Modern Java code may use lambdas when the listener is a functional interface.
Inner Class and Memory Relationship
A non-static inner class maintains an association with an instance of its enclosing class.
Conceptually:
Outer Object
↑
│
Inner ObjectBecause of this relationship, a non-static inner class can access the outer instance's members.
A static nested class does not have this automatic enclosing-instance relationship.
This distinction can matter when designing long-lived objects because holding an inner-class object can keep its associated outer object reachable.
Member Inner Class vs Static Nested Class
| Feature | Member Inner Class | Static Nested Class |
|---|---|---|
Declared with static | No | Yes |
| Requires outer instance | Yes | No |
| Has automatic outer-instance relationship | Yes | No |
| Direct access to outer instance fields | Yes | No |
| Direct access to outer static members | Yes | Yes |
| Creation | outer.new Inner() | new Outer.Nested() |
Local Inner Class vs Anonymous Inner Class
| Feature | Local Inner Class | Anonymous Inner Class |
|---|---|---|
| Has a name | Yes | No |
| Declared inside method/block | Yes | Usually inside an expression |
| Can define multiple methods | Yes | Yes |
| Can be reused within its scope | Yes | Usually created for one use |
| Useful for | Local reusable logic | One-time implementation |
Inner Class vs Static Nested Class
Consider:
class Outer {
int value = 100;
class Inner {
void display() {
System.out.println(value);
}
}
static class Nested {
void display() {
// Cannot directly access value
}
}
}The difference comes from whether the nested class has an enclosing outer object.
When Should You Use Inner Classes?
Inner classes can be useful when:
- A helper class is meaningful only within one outer class.
- The nested class needs access to the outer object's state.
- You want to hide implementation details.
- A small implementation is needed for a particular operation.
- A class has a strong logical relationship with its enclosing class.
For example:
BankAccount
└── Transactionor:
Tree
└── NodeThe nested class can make that relationship explicit.
When Should You Avoid Inner Classes?
Avoid unnecessarily deep or complicated nesting.
For example:
Outer
└── Inner
└── Inner2
└── Inner3can make code difficult to maintain.
If the nested class represents an important independent concept, a top-level class may be clearer.
Common Mistakes
Mistake 1: Creating a Non-Static Inner Class Incorrectly
Incorrect:
Outer.Inner inner =
new Outer.Inner();Correct:
Outer outer = new Outer();
Outer.Inner inner =
outer.new Inner();Mistake 2: Confusing Static Nested Classes with Inner Classes
Strictly speaking:
static class Nestedis a static nested class, not a non-static inner class.
The term "nested class" includes both.
Mistake 3: Accessing Instance Members from a Static Nested Class
This is invalid:
class Outer {
int value = 100;
static class Nested {
void display() {
System.out.println(value);
}
}
}The static nested class does not automatically have an Outer instance.
Mistake 4: Modifying a Local Variable Captured by a Local Class
This is not allowed:
void method() {
int value = 10;
class Local {
void display() {
System.out.println(value);
}
}
value = 20;
}The local variable must be final or effectively final when accessed from the local class.
Mistake 5: Overusing Anonymous Classes
Anonymous classes are useful for small, focused implementations.
If the implementation becomes large, a named class may make the code easier to understand and maintain.
Important Syntax
Member inner class
class Outer {
class Inner {
}
}Static nested class
class Outer {
static class Nested {
}
}Local inner class
class Outer {
void method() {
class Local {
}
}
}Anonymous inner class
Interface obj = new Interface() {
@Override
public void method() {
}
};Quick Comparison of All Types
| Type | Where Declared | Name | Outer Instance Required |
|---|---|---|---|
| Member inner class | Inside class | Yes | Yes |
| Static nested class | Inside class | Yes | No |
| Local inner class | Inside method/block | Yes | It can access enclosing instance when applicable |
| Anonymous inner class | Inside expression | No | Depends on context |
Frequently Asked Questions
What is an inner class?
An inner class is a non-static nested class declared inside another class.
How many types of inner/nested classes are there?
The commonly discussed types are:
- Member inner class
- Static nested class
- Local inner class
- Anonymous inner class
Strictly speaking, the static nested class is not an inner class because it is static.
Can an inner class access private members of the outer class?
Yes.
class Outer {
private int value = 100;
class Inner {
void display() {
System.out.println(value);
}
}
}Can an inner class have static members?
Modern Java permits static members in nested classes, subject to the language rules for the particular kind of nested class and Java version. A static nested class can naturally contain static members.
Can an inner class extend another class?
Yes.
class Inner extends Parent {
}Can an inner class implement an interface?
Yes.
class Inner implements Runnable {
}Can an inner class be private?
Yes, a member inner class can have private access.
What is Outer.this?
Outer.this refers to the current instance of the enclosing outer class.
What is an anonymous inner class?
It is a class without a declared name that is defined and instantiated at the same time.
What is a static nested class?
A nested class declared with static. It does not require an instance of the outer class.
Key Points
- A class declared inside another class is a nested class.
- A non-static nested class is called an inner class.
- Java supports member, local, and anonymous inner classes.
- Java also supports static nested classes.
- A member inner class requires an instance of its outer class.
Outer.thisrefers to the enclosing outer object.- Inner classes can access outer-class members, including private members.
- Static nested classes do not require an outer-class object.
- Static nested classes cannot directly access outer instance members.
- Local inner classes are declared inside methods or blocks.
- Local inner classes can access final or effectively final local variables.
- Anonymous inner classes have no declared class name.
- Anonymous classes are useful for one-time implementations.
- Functional interfaces can often be implemented more concisely with lambdas.
- Inner classes can help encapsulate closely related implementation details.
- Deeply nested classes should be avoided when they make the design harder to understand.
Conclusion
Inner classes provide a way to keep closely related classes together and can be useful for encapsulation, organization, and localized behavior.
The overall structure can be remembered as:
Nested Classes
│
├── Static Nested Class
│
└── Inner Classes
│
├── Member Inner Class
├── Local Inner Class
└── Anonymous Inner ClassThe most important difference is whether the nested class has an automatic relationship with an instance of the outer class:
Member Inner Class
↓
Requires Outer Object
Static Nested Class
↓
Does Not Require Outer Object
Local Inner Class
↓
Limited to Local Scope
Anonymous Inner Class
↓
One-Time Unnamed ImplementationOnce these concepts are clear, you can understand how Java uses nested types for encapsulation, helper implementations, callbacks, event handling, and other object-oriented designs.
See you in next page....🙂