Java Inner Classes

In the last lesson you learned about Java enum methods. Now we take “a class with extra powers” further. You can put one class inside another class. And one kind, the inner class, can reach right into the object that surrounds it.

🤔 Why put a class inside a class?

Picture a Notebook that keeps a list of pages. Each page belongs to one notebook and makes no sense on its own.

  • A separate top-level Page class looks like a big public thing, when it is really a private helper.
  • If a page needs the notebook’s settings, you would hand it a reference to the notebook every time.
  • A nested class fixes this. You write the helper right inside the class that owns it.
  • When the helper also needs the outer object’s data, you make it a non-static inner class. That is today’s topic.

🧱 An inner class is tied to an outer object

A non-static nested class is called an inner class. The key idea: every inner-class object is born attached to one outer-class object. It cannot exist on its own. Here an Engine lives inside a Car.

public class Car {
String model = "Tesla";
class Engine { // inner class, no static keyword
void describe() {
System.out.println("Engine of " + model); // reads the outer field
}
}
public static void main(String[] args) {
Car car = new Car(); // first make the outer object
Car.Engine engine = car.new Engine(); // then make the inner one from it
engine.describe();
}
}

The two lines in main show the whole rule.

  • First build the outer object: new Car().
  • Then build the inner object from that car: car.new Engine().
  • The engine belongs to that one car, so it can read the car’s model.
  • The type is written Car.Engine, the outer name in front. No static keyword is what makes it an inner class.

Output

Engine of Tesla

🔑 An inner class can read the outer object’s fields, even private ones

This is the real reason inner classes exist. An inner-class object quietly holds a hidden link back to its outer object. Through that link, it can use the outer object’s fields and methods directly, even private ones. Here a private field and a private method are used straight from the inner class.

public class BankAccount {
private double balance = 500; // private outer field
private void log(String msg) { // private outer method
System.out.println("LOG: " + msg);
}
class Statement {
void print() {
log("printing statement"); // calls the outer private method
System.out.println("Balance is " + balance); // reads outer private field
}
}
public static void main(String[] args) {
BankAccount account = new BankAccount();
BankAccount.Statement s = account.new Statement();
s.print();
}
}

print never received the balance as an argument.

  • The Statement object keeps a hidden link to its BankAccount.
  • Through that link it reaches balance and log(...) by name.
  • Private does not block it, because the inner class is part of the same outer class.

Output

LOG: printing statement
Balance is 500.0

This close access is the whole point. The inner class works on behalf of one outer object, so Java lets it see everything that object has.

🛠️ A real use: a linked list with a Node helper

Inner classes shine for a helper that only makes sense inside one bigger structure. A linked list stores its items in small boxes called nodes, each pointing to the next. A Node is useless outside its list, so it belongs inside.

public class IntList {
private Node head; // first node, or null when empty
class Node { // inner helper class
int value;
Node next;
Node(int value) {
this.value = value;
}
}
void add(int value) {
Node fresh = new Node(value); // inside the outer class we can just write new Node
fresh.next = head;
head = fresh;
}
void printAll() {
Node current = head;
while (current != null) {
System.out.print(current.value + " ");
current = current.next;
}
System.out.println();
}
public static void main(String[] args) {
IntList list = new IntList();
list.add(10);
list.add(20);
list.add(30);
list.printAll();
}
}

One detail. Inside add we wrote new Node(value) with no car.new prefix. That works because add is an instance method of IntList, so an outer object already exists. You only need the outer.new Inner() form when you are outside the outer class.

Output

30 20 10

🔁 Another use: an Iterator that walks the outer object

An iterator is a small object whose only job is to step through a collection one item at a time. It needs the collection’s data, so an inner class is ideal. This Bag holds a few names. Its inner BagIterator walks through them, reading the outer array directly.

public class Bag {
private String[] items = { "pen", "book", "cup" };
class BagIterator {
private int index = 0; // where we are in the walk
boolean hasNext() {
return index < items.length; // reads the outer array
}
String next() {
return items[index++]; // returns one item, then moves on
}
}
public static void main(String[] args) {
Bag bag = new Bag();
Bag.BagIterator it = bag.new BagIterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}

The iterator keeps its own index, but the data stays in the Bag. Because BagIterator is an inner class, it reads items straight away. No need to copy the array or pass it in.

Output

pen
book
cup

📍 A quick look at local classes

One more flavour: a local class, declared inside a method rather than at the top of the outer class. Use it when a helper is needed in just one method.

public class Demo {
void run(String name) {
class Greeter { // local class, lives only inside run
void sayHi() {
System.out.println("Hi " + name); // can use the method's variable
}
}
Greeter g = new Greeter(); // make it normally, right here
g.sayHi();
}
public static void main(String[] args) {
new Demo().run("Sam");
}
}

A local class is created with a plain new Greeter() because you are already inside the method. It can also read the method’s local variables, like name. Keep local classes for tiny one-off helpers. If one grows, move it to a named class.

Output

Hi Sam

🔀 Inner class vs static nested class

We cover static nested classes fully next lesson, but here is the core difference.

  • An inner class has no static keyword. It is tied to an outer object and needs one to exist.
  • A static nested class has the static keyword. It needs no outer object, and cannot reach the outer object’s instance fields.

This pair shows the contrast side by side.

public class Outer {
private int value = 7;
class Inner { // inner: needs an Outer, can read value
void show() {
System.out.println(value); // ✅ reads the outer instance field
}
}
static class Nested { // static nested: no Outer needed
void show() {
System.out.println("no outer object here");
// System.out.println(value); // ❌ cannot reach an instance field
}
}
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // needs an outer object
inner.show();
Outer.Nested nested = new Outer.Nested(); // no outer object needed
nested.show();
}
}

The two creation lines say it all. outer.new Inner() starts from an existing outer object. new Outer.Nested() does not. Use an inner class when the helper truly needs the outer object’s data, a static nested class when it does not.

Output

7
no outer object here

Quick test

Ask one question: does this helper need the outer object’s instance fields? If yes, make it an inner class. If no, add static and make it a static nested class. The default choice for most helpers is static.

⚠️ Common Mistakes

Trying to create an inner class with a plain new. From outside the outer class, an inner class needs an outer object first. A bare new will not compile.

// ❌ Avoid: no outer object, so this fails from outside Outer
Outer.Inner inner = new Outer.Inner();
// ✅ Good: make the outer object, then make the inner one from it
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

Making it an inner class when it does not need the outer object. A non-static inner class secretly carries a link to the outer object. If the helper never uses it, that link is extra weight and keeps the outer object alive longer than needed. Add static instead.

// ❌ Avoid: inner class that never touches the outer object
public class Tree {
class Node { // no outer field is ever used here
int value;
}
}
// ✅ Good: it needs no outer object, so make it static nested
public class Tree {
static class Node {
int value;
}
}

Confusing the inner type’s name. The inner class name carries the outer name in front. Writing just Engine from outside the class will not be found.

// ❌ Avoid: the bare name is not visible outside Car
Engine engine = car.new Engine();
// ✅ Good: use the full nested name Car.Engine
Car.Engine engine = car.new Engine();

✅ Best Practices

  • Reach for an inner class only when the helper needs the outer object’s data. If not, prefer a static nested class.
  • Keep inner classes small and private when nothing outside should touch them.
  • Create them with outer.new Inner() from outside, plain new Inner() from inside.
  • Use local classes for true one-off helpers. If one grows, promote it to a named class.
  • Default to static for nested classes. Most helpers do not need the outer object. Drop static only when you truly need the link.

🧩 What You’ve Learned

Great work. Let’s recap inner classes.

  • ✅ An inner class is a non-static class defined inside another class.
  • ✅ Each inner-class object is tied to one outer object and holds a hidden link to it.
  • ✅ Through that link, an inner class can read the outer object’s fields and methods, even private ones.
  • ✅ From outside, you create one with outer.new Inner(); from inside an instance method, a plain new Inner() works.
  • ✅ Inner classes fit helpers that need the outer’s state, like a Node in a list or an iterator.
  • ✅ A local class is declared inside a method for a one-off helper.
  • ✅ A static nested class has the static keyword, needs no outer object, and cannot reach instance fields.

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What makes a nested class an inner (non-static) class?

    Why: An inner class has no static keyword. Each of its objects is attached to one outer object and can use that object's members.

  2. 2

    From outside the outer class, how do you create an inner-class object?

    Why: An inner class needs an outer object first, so you write outer.new Inner() using an existing outer instance.

  3. 3

    Can an inner class read a private field of its outer object?

    Why: An inner class is part of the outer class, so it can use the outer object's members directly, including private ones.

  4. 4

    When should you use a static nested class instead of an inner class?

    Why: If the helper never uses the outer object's data, make it static. A static nested class needs no outer object and avoids the hidden link.

🚀 What’s Next?

An inner class is great when it needs the outer object. But many nested helpers, like a Node or a Builder, never touch the outer object’s data at all. For those, you add the static keyword and skip the link entirely. Let’s see how that works.

Java Static Nested Classes

Share & Connect