You can write an ordinary Java class, but what changes when one appears inside another? A true inner class differs from a static nested class. Object creation, captured values and printed output all change with the form: one of them cannot be constructed at all without an existing outer object, and another stops compiling the moment a captured local is reassigned.
Inner classes in Java: the terminology and the choice map
A nested class is any class declared inside another class. Member inner, local and anonymous classes are inner classes whose instances have an enclosing context. A static nested class is not technically an inner class because it has no implicit enclosing instance.
Form | Declaration location | Outer object required? | Enclosing state it can access | Reusable type name? | Best use case |
|---|---|---|---|---|---|
Member inner | Outer-class member | Yes | Outer instance fields, including private fields | Yes | Result calculation tied to one |
Static nested | Static outer-class member | No | Outer static members directly | Yes | Temperature conversion independent of an outer object |
Local | One block or method | No in this static method example | Effectively final locals and available enclosing context | Only in its block | A score helper needed in one method |
Anonymous | Object-creation expression | No in this static method example | Effectively final locals and available enclosing context | No | A one-off descending comparator |
Read the third column first: it splits the four forms into those that carry an enclosing instance and those that do not. A static nested class never carries one, a member inner class always does, and a local or anonymous class carries one only when it is declared inside an instance method. That single distinction decides most of what follows, and it is the same distinction behind the comparator and node-class questions set in the Coding and DSA courses.
Member inner class: calculate 365 out of 500 as 73.0 percent
Save this as ExamResult.java:
public class ExamResult {
private int maximumMarks = 500;
class PercentageCalculator {
double calculate(int scoredMarks) {
return scoredMarks * 100.0 / maximumMarks;
}
}
public static void main(String[] args) {
ExamResult result = new ExamResult();
ExamResult.PercentageCalculator calculator =
result.new PercentageCalculator();
System.out.printf("percentage=%.1f%n", calculator.calculate(365));
}
}result refers to one ExamResult whose private maximumMarks is 500. result.new PercentageCalculator() creates an inner object tied to that object. It reads the private field, so 365 * 100.0 / 500 = 73.0.
The exact output is:
percentage=73.0You cannot write new PercentageCalculator() directly in static main because no current ExamResult exists. Use result.new PercentageCalculator(). A member inner class should genuinely need outer-instance state.

Static nested class: convert 25 degrees Celsius to 77 degrees Fahrenheit
Save this separately as Temperature.java:
public class Temperature {
private static int offset = 32;
static class Converter {
static int cToF(int celsius) {
return celsius * 9 / 5 + offset;
}
}
public static void main(String[] args) {
System.out.println(Temperature.Converter.cToF(25));
}
}Java integer arithmetic gives 25 * 9 = 225, 225 / 5 = 45, then 45 + 32 = 77. The output is 77. No Temperature object is needed. Converter reads static offset, not an instance field.
Use this form when a helper belongs in the outer namespace but needs no outer object. A binary tree class usually keeps its Node as a static nested type, because a node holds only its own key and its child links and never reads the tree object. That is the structure walked through in Binary Trees and Binary Search Trees.
Local inner class: capture 85 and 10 to produce 95
A local class keeps a full helper body inside its one method:
public class LocalClassDemo {
public static void main(String[] args) {
int baseScore = 85;
int bonus = 10;
class ScoreCard {
int finalScore() {
return baseScore + bonus;
}
}
ScoreCard card = new ScoreCard();
System.out.println(card.finalScore());
}
}ScoreCard is usable only in its declaration block. It reads baseScore and bonus because both are effectively final. Thus 85 + 10 = 95, and the exact output is 95.
Writing bonus++; after initialisation makes bonus non-effectively-final, so capture fails at compile time. If another method needs ScoreCard, promote it to a member or top-level type.
Anonymous inner class: sort 72, 91 and 65 in descending order
This program creates a one-off comparator:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class RankScores {
public static void main(String[] args) {
List<Integer> scores = new ArrayList<>(Arrays.asList(72, 91, 65));
scores.sort(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return Integer.compare(b, a);
}
});
System.out.println(scores);
}
}The expression declares an unnamed Comparator<Integer> implementation and creates its one object. Comparing 91 with 72 calls Integer.compare(72, 91). Its negative result puts 91 first. Comparing 65 against larger values leaves it last. Output is exactly [91, 72, 65].
A lambda can replace it because Comparator is a functional interface. Keep an anonymous class for a fuller body or its own object identity. Apply the ordering idea in Sorting Algorithms: Complexity and Comparison.
Java inner-class scope: distinguish 2, 7 and 40
Scope resolution distinguishes the parameter from the inner and enclosing fields:
public class ScopeDemo {
int value = 40;
class Inner {
int value = 7;
void print(int value) {
System.out.println(value);
System.out.println(this.value);
System.out.println(ScopeDemo.this.value);
}
}
public static void main(String[] args) {
ScopeDemo demo = new ScopeDemo();
Inner inner = demo.new Inner();
inner.print(2);
}
}Bare value is parameter 2. this.value is the Inner field 7. ScopeDemo.this.value is the enclosing field 40. In an inner-class instance method, this means the inner object. The separate output lines are 2, 7, 40.
Common traps have direct fixes:
new ExamResult.PercentageCalculator()is invalid because it supplies no outer object. Useresult.new PercentageCalculator().A static nested class cannot read a non-static outer field directly. Supply an outer reference or use a member inner class.
Mutating a captured local breaks effective finality. Keep it unchanged or put mutable state in an object.
An anonymous class has no name, so it cannot declare a constructor. Use an instance initialiser or named class.

Inner-class questions: predict construction, access and output
Java tracing questions ask whether an outer instance is required, which shadowed value is read, or why capture fails. They also test static nested versus member inner and the choice among named, local, anonymous and lambda forms. The same three shapes come back in coursework assignments, placement rounds and interviews.
Check your reasoning with three changes:
Predict the construction first:
new PercentageCalculator()inside staticmaindoes not compile, whileresult.new PercentageCalculator().calculate(400)gives80.0because400 * 100.0 / 500 = 80.0.Temperature.Converter.cToF(30)gives86because30 * 9 / 5 + 32 = 86.Changing the comparator to
Integer.compare(a, b)sorts the original[72, 91, 65]as[65, 72, 91].
KnowledgeGate carries over 300 Java practice questions built on exactly these tracing and design decisions.
Inner classes in Java: the short version and next step
If a helper needs one outer object's state, use a member inner class. If it needs the namespace but no outer object, use a static nested class. For one block, use a local class. For a one-off implementation, use an anonymous class or suitable lambda.
Make two modifications. Set maximumMarks to 800 and call calculate(612): 612 * 100.0 / 800 = 76.5. Keep baseScore = 85 and set bonus = 15: 85 + 15 = 100. Appending bonus++ then breaks capture because bonus is no longer effectively final.
Continue with the Java Course, Concepts, MCQs and Coding Questions. Rerun the four form examples and the ScopeDemo trace, change one input at a time, and predict each output before compiling.




