☕ Java
Static Nested Classes
A static nested class is a class declared with the static modifier inside another class. Unlike inner classes, it has no implicit reference to an enclosing instance and can be instantiated without an enclosing object. It behaves like a top-level class that happens to be scoped inside another class for organizational purposes. Static nested classes are the cleanest and most commonly recommended form of nesting: they provide the organizational and access benefits of nesting without the memory overhead and lifecycle coupling of inner classes. This entry covers the mechanics, access rules, instantiation, common patterns, and the key reasons static nested classes are preferred over inner classes in most situations.
Mechanics and Access Rules
A static nested class is declared with the static keyword and therefore behaves like a regular top-level class in almost every respect. It does not hold an implicit reference to an enclosing instance, which means it can be instantiated without an enclosing object, its instances are not tied to the lifecycle of any enclosing instance, and it can have its own static members (which non-static inner classes cannot have in most forms).
Despite the lack of an enclosing instance reference, a static nested class still has privileged access to the private members of its enclosing class — but only to static private members when accessed directly, or to instance private members if it has a reference to an enclosing object that it obtained some other way. The access is to the private scope, not through any implicit outer reference.
The instantiation syntax for a static nested class is simply OuterClass.NestedClass, exactly like any other qualified class name. No outer instance is required. This is the most visible practical difference from inner classes: inner.new Syntax() requires an outer instance, while new Outer.StaticNested() does not. The static nested class is accessible wherever the enclosing class is accessible (subject to its own access modifier), and its full qualified name is the outer class name dot the nested class name.
Static nested classes can themselves contain static and instance members, including further nested classes. There is no restriction on nesting depth, though deeply nested static classes are a design smell — if a class is three levels deep in nesting, it has probably outgrown its role as a private implementation detail.
Java
// ── Static nested class fundamentals ────────────────────────────────
public class Configuration {
// ── Static fields of enclosing class ─────────────────────────────
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 8080;
// ── Instance fields of enclosing class ───────────────────────────
private String host;
private int port;
// ── Private constructor — force use of builder ────────────────────
private Configuration(Builder builder) {
this.host = builder.host;
this.port = builder.port;
}
// ── Static nested class — accessed as Configuration.Builder ───────
// No instance of Configuration required to create a Builder
public static class Builder {
private String host = DEFAULT_HOST; // access outer STATIC field
private int port = DEFAULT_PORT; // access outer STATIC field
public Builder host(String host) { this.host = host; return this; }
public Builder port(int port) { this.port = port; return this; }
public Configuration build() {
if (port < 1 || port > 65535)
throw new IllegalArgumentException("Invalid port: " + port);
return new Configuration(this); // can call private constructor!
}
}
public String getHost() { return host; }
public int getPort() { return port; }
@Override public String toString() {
return host + ":" + port;
}
}
// ── Instantiation — no outer instance needed ──────────────────────────
Configuration config = new Configuration.Builder() // direct instantiation
.host("myapp.example.com")
.port(443)
.build();
System.out.println(config); // myapp.example.com:443
// ── Contrast with inner class instantiation ───────────────────────────
// Inner: requires existing outer instance: outer.new Inner()
// Static nested: independent: new Outer.StaticNested()
// ── Static nested class with its own static members ───────────────────
public class Cache<K, V> {
private final Map<K, V> store = new HashMap<>();
// ── Static nested — CacheEntry has no need for Cache instance ────
public static class CacheEntry<K, V> {
private final K key;
private final V value;
private final Instant createdAt;
private static long totalEntries = 0; // own static member
public CacheEntry(K key, V value) {
this.key = key;
this.value = value;
this.createdAt = Instant.now();
totalEntries++;
}
public K getKey() { return key; }
public V getValue() { return value; }
public Instant getCreatedAt() { return createdAt; }
public boolean isExpired(Duration ttl) {
return createdAt.plus(ttl).isBefore(Instant.now());
}
public static long getTotalEntries() { return totalEntries; }
}
}Why Static Nested Classes Are Preferred
The preference for static nested classes over inner classes comes from four concrete advantages. First, no hidden memory overhead: an inner class instance holds a reference to its enclosing instance, keeping the enclosing object alive as long as the inner class instance lives. A static nested class has no such reference, so its lifecycle is independent. This eliminates an entire category of memory leaks.
Second, simpler instantiation: a static nested class can be created anywhere, by anyone with access to the outer class name, without requiring an existing outer instance. Inner classes require an outer instance at the call site, which complicates factory methods, serialisation, and frameworks that instantiate objects reflectively.
Third, no unintentional coupling: when a non-static inner class is used, the implicit outer reference creates a coupling that may be invisible in the source code but has real runtime consequences. Static nested classes make the coupling explicit — if the nested class needs the outer class's data, it must receive it as a constructor parameter or method argument. This explicitness makes the dependency visible and auditable.
Fourth, can have static members: static nested classes can declare static fields and static methods. Non-static inner classes cannot have static members (with minor exceptions for compile-time constants), which sometimes forces awkward workarounds. The static nested class has the full member capability of a top-level class.
The guidance is simple: when adding a nested class, default to static. Make it non-static only when there is a specific, genuine need for the implicit enclosing instance reference — when the nested class regularly accesses instance members of the outer class and it would be more cumbersome to pass that access explicitly.
Java
// ── Static nested vs inner: the memory and lifecycle difference ──────
public class MemoryDemo {
private byte[] largeData = new byte[10 * 1024 * 1024]; // 10MB
// ── Inner class: holds implicit reference to MemoryDemo instance ──
public class InnerCallback implements Runnable {
@Override
public void run() {
System.out.println("Callback ran — outer alive: " +
largeData.length + " bytes kept alive");
}
}
// ── Static nested: no reference to MemoryDemo ────────────────────
public static class StaticCallback implements Runnable {
private final String message; // explicit parameter instead
public StaticCallback(String message) {
this.message = message;
}
@Override
public void run() {
System.out.println("Static callback: " + message);
// largeData is NOT kept alive — no outer reference
}
}
}
// ── Memory leak scenario ─────────────────────────────────────────────
MemoryDemo demo = new MemoryDemo();
// Storing InnerCallback in a long-lived registry keeps demo alive
// — 10MB stays in memory as long as the callback is registered
callbackRegistry.register(demo.new InnerCallback());
// StaticCallback does not keep demo alive
// — demo (and its 10MB) can be garbage collected independently
callbackRegistry.register(new MemoryDemo.StaticCallback("task done"));
// ── Explicit dependency is better than implicit ───────────────────────
// INNER: implicit access to outer — coupling is hidden
public class Report {
private final String title;
private final List<Section> sections = new ArrayList<>();
public class SectionBuilder {
// Implicitly accesses Report.this.sections — hidden coupling
public void addTo(String content) {
sections.add(new Section(content)); // outer's sections
}
}
}
// STATIC NESTED: explicit dependency — coupling is visible
public class Report {
private final String title;
private final List<Section> sections = new ArrayList<>();
public static class SectionBuilder {
private final List<Section> target; // explicit reference
public SectionBuilder(List<Section> target) {
this.target = target; // explicit — readable, testable
}
public void addTo(String content) {
target.add(new Section(content));
}
}
public SectionBuilder newSectionBuilder() {
return new SectionBuilder(sections); // pass explicit reference
}
}Design Patterns Using Static Nested Classes
Several canonical Java design patterns use static nested classes as their implementation mechanism. The builder pattern is the most ubiquitous: a static nested Builder class constructs instances of the enclosing class, has access to the private constructor, and is naturally scoped inside the class it builds. The Lombok @Builder annotation generates exactly this structure.
The entry type for Map is a classic example from the JDK itself: Map.Entry<K,V> is logically a part of Map, has no meaning outside of Map, and is therefore a static nested interface inside Map. Every Map implementation provides its own Entry implementation.
The node type for linked data structures (LinkedList.Node, TreeMap.Entry) is another common pattern. Nodes are implementation details of the data structure, have no meaning outside of it, but also have no need for an outer instance reference — they are structural components that are woven together by references, not by implicit outer references. Static nested classes model this perfectly.
Java
// ── Builder pattern with static nested class ─────────────────────────
public final class Email {
private final String from;
private final List<String> to;
private final List<String> cc;
private final String subject;
private final String body;
private final boolean html;
private Email(Builder b) {
this.from = b.from;
this.to = List.copyOf(b.to);
this.cc = List.copyOf(b.cc);
this.subject = b.subject;
this.body = b.body;
this.html = b.html;
}
public static Builder from(String from) { return new Builder(from); }
public static final class Builder {
private final String from;
private final List<String> to = new ArrayList<>();
private final List<String> cc = new ArrayList<>();
private String subject = "";
private String body = "";
private boolean html = false;
private Builder(String from) {
this.from = Objects.requireNonNull(from, "from is required");
}
public Builder to(String... addresses) {
Collections.addAll(this.to, addresses);
return this;
}
public Builder cc(String... addresses) {
Collections.addAll(this.cc, addresses);
return this;
}
public Builder subject(String subject) {
this.subject = subject;
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public Builder htmlBody(String html) {
this.body = html;
this.html = true;
return this;
}
public Email build() {
if (to.isEmpty()) throw new IllegalStateException(
"At least one recipient required");
return new Email(this);
}
}
public String getFrom() { return from; }
public List<String> getTo() { return to; }
public String getSubject() { return subject; }
public String getBody() { return body; }
public boolean isHtml() { return html; }
}
// ── Map.Entry pattern — nested type that models a relationship ─────────
public class SimpleMap<K, V> {
// ── Static nested — Entry has no need for SimpleMap instance ──────
public static class Entry<K, V> {
private final K key;
private V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
public void setValue(V value) { this.value = value; }
@Override
public String toString() { return key + "=" + value; }
}
private final List<Entry<K, V>> entries = new ArrayList<>();
public void put(K key, V value) {
for (Entry<K, V> e : entries) {
if (e.getKey().equals(key)) { e.setValue(value); return; }
}
entries.add(new Entry<>(key, value));
}
public Set<Entry<K, V>> entrySet() {
return new HashSet<>(entries);
}
}
// ── Usage — clear, independent construction ───────────────────────────
Email email = Email.from("sender@example.com")
.to("alice@example.com", "bob@example.com")
.cc("manager@example.com")
.subject("Q3 Report Ready")
.htmlBody("<h1>Report attached</h1>")
.build();
SimpleMap.Entry<String, Integer> entry =
new SimpleMap.Entry<>("score", 95); // No SimpleMap neededRelated Topics in Object-Oriented Programming
OOP Concepts
Object-Oriented Programming (OOP) is a programming paradigm that organises software around objects — self-contained units that combine data (fields) and behaviour (methods). Java is a class-based, object-oriented language where almost everything is an object. OOP provides four foundational principles: encapsulation, inheritance, polymorphism, and abstraction. Together they produce software that is modular, reusable, maintainable, and easier to reason about as systems grow in complexity.
Classes
A class is the fundamental building block of Java. It is a blueprint that defines the structure and behaviour of objects — what data each object holds (fields) and what operations it can perform (methods). Every Java program is composed of classes. Understanding how to design a class well — choosing the right access modifiers, separating state from behaviour, and writing cohesive single-responsibility classes — is the foundation of object-oriented programming in Java. This entry covers class anatomy, fields, methods, access modifiers, static vs instance members, the this keyword, and class design principles.
Objects
An object is a runtime instance of a class. Where a class is a blueprint that exists in source code, an object is a living entity that exists in memory during program execution — it has its own identity, its own state stored in its fields, and the ability to respond to method calls. Every object has three fundamental properties: identity (a unique memory address), state (the current values of its fields), and behaviour (the methods it responds to). This entry covers object identity vs equality, the Object class hierarchy, object state and mutation, method calls, toString, equals and hashCode, and the object lifecycle.
Object Creation
Object creation in Java is the process of allocating memory, initialising fields, and running constructor logic to bring an object into existence. The new keyword is the primary mechanism, but Java also provides factory methods, builder patterns, copy constructors, and object cloning. Constructors are special methods that set up the initial state — their design determines how easy or difficult the class is to use correctly. This entry covers constructors in depth, constructor overloading and chaining, copy constructors, factory methods, the builder pattern, and the difference between shallow and deep copy.