☕ Java
Anonymous Object
An anonymous object is an object that is created and used without being assigned to a named reference variable. The object is created with the new keyword, used immediately (typically by calling a method on it or passing it as an argument), and then becomes eligible for garbage collection because no variable holds a reference to it. Anonymous objects are a convenience feature that reduces boilerplate when an object is needed only once for a single operation.
What an Anonymous Object Is
In Java, every object is created with new ClassName(arguments). Normally, the resulting reference is stored in a variable so you can use the object multiple times. But sometimes an object is needed only once — to call a single method, to pass as an argument, or to return from a method. In these cases, assigning the object to a variable and then immediately using it once is unnecessary verbosity. An anonymous object skips the variable assignment.
The word "anonymous" means "without a name" — the object has no variable name by which it can be referenced after its initial use. The expression new ClassName() creates the object but immediately uses it, and because no variable holds the reference, the object becomes eligible for garbage collection as soon as the expression is evaluated.
Anonymous objects are not a special language feature requiring special syntax — they are simply the natural result of using a new expression directly without assignment. Every Java developer uses them constantly without necessarily thinking of them by name: System.out.println(new Date()), Arrays.sort(new int[]{3,1,2}), and new Thread(() -> runTask()).start() all involve anonymous objects.
Understanding anonymous objects matters for two reasons. First, it helps you write more concise code by recognising when a variable is unnecessary. Second, it prevents misuse — anonymous objects are appropriate only when the object is truly needed only once. Using anonymous objects where the object should be reused forces redundant object creation and is a performance and correctness mistake.
Java
// ── Named object — variable holds the reference: ─────────────────────
StringBuilder namedBuilder = new StringBuilder();
namedBuilder.append("Hello");
namedBuilder.append(" World");
String result = namedBuilder.toString();
System.out.println(result);
// ── Anonymous object — used once, no variable: ────────────────────────
// Object created → method called → result used → object eligible for GC
System.out.println(new StringBuilder("Hello World").toString());
// ── The lifecycle of an anonymous object: ────────────────────────────
// 1. new StringBuilder("Hello") → object created on heap
// 2. .append(" World") → method called on the new object
// 3. .toString() → another method called
// 4. println(...) → result used
// 5. Expression evaluated → no variable holds the object
// 6. Object becomes unreachable → eligible for garbage collection
// ── Comparison — both produce the same result: ────────────────────────
// Named:
StringBuilder sb = new StringBuilder();
sb.append("Name: ").append("Alice").append(", Age: ").append(30);
System.out.println(sb.toString());
// Anonymous — no sb variable needed:
System.out.println(
new StringBuilder().append("Name: ").append("Alice")
.append(", Age: ").append(30).toString()
);
// Both output: Name: Alice, Age: 30Common Uses of Anonymous Objects
Anonymous objects appear in several recurring patterns in Java code. Passing arguments to methods is the most common: new BigDecimal("99.99") in a method call is an anonymous object. Creating and using a single-use object in one expression avoids a variable that adds nothing to readability. Returning objects directly from methods without naming them first is clean and idiomatic.
Anonymous objects are particularly natural for immutable value objects. Since an immutable object cannot change after creation, there is no need to hold a reference to modify it later. Creating and passing new Point(3, 4) directly is perfectly safe and correct — the same as creating it, naming it, passing it, and never using the variable again.
In testing code, anonymous objects are extensively used to construct test data inline. Assertions that compare with new Address("123 Main", "London") directly are cleaner than declaring a local variable that is used only in the assertion. Test frameworks like JUnit and AssertJ are designed to work naturally with anonymous objects.
Method chaining — calling multiple methods on a new object and discarding the final result — is another common pattern: new Timer().scheduleAtFixedRate(task, 0, 1000) creates a Timer, schedules a task, and the Timer continues running in the background. The Timer variable is not needed because the Timer manages itself after scheduling.
Java
// ── 1. Passing anonymous object as method argument: ──────────────────
public static void printDetails(Person person) {
System.out.println("Name: " + person.getName() +
", Age: " + person.getAge());
}
// Named — variable created just to be passed immediately:
Person namedPerson = new Person("Alice", 30);
printDetails(namedPerson); // namedPerson never used again
// Anonymous — cleaner when object is used only as an argument:
printDetails(new Person("Alice", 30));
// ── 2. Returning anonymous object from a method: ──────────────────────
public static Point midpoint(double x1, double y1, double x2, double y2) {
return new Point((x1 + x2) / 2, (y1 + y2) / 2); // anonymous
}
// ── 3. Anonymous object in collections: ──────────────────────────────
List<Person> team = new ArrayList<>();
team.add(new Person("Alice", 30)); // all anonymous
team.add(new Person("Bob", 25));
team.add(new Person("Charlie", 35));
// ── 4. Chained method calls on anonymous object: ─────────────────────
String result = new StringBuilder()
.append("Hello")
.append(", ")
.append("World")
.append("!")
.toString();
System.out.println(result); // Hello, World!
// ── 5. Anonymous object in stream operations: ─────────────────────────
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream()
.map(name -> new Person(name, 0)) // anonymous Person in map
.map(Person::getName)
.forEach(System.out::println);
// ── 6. Starting a thread with an anonymous Runnable object: ───────────
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Running in: " + Thread.currentThread().getName());
}
}).start(); // Thread object is anonymous — fire and forget
// Modern equivalent with lambda (anonymous Runnable):
new Thread(() -> System.out.println("Lambda thread")).start();
// ── 7. Anonymous objects in test assertions: ──────────────────────────
// In JUnit tests — creating expected values inline:
assertEquals(new Point(3.0, 4.0), midpoint(0, 0, 6, 8));
assertEquals(new Address("123 Main", "London", "EC1A 1BB"),
service.findAddressForUser("alice"));When to Use and When to Avoid Anonymous Objects
Anonymous objects are appropriate when an object is genuinely needed only once and will never be referenced again. The decision criterion is simple: if you find yourself writing a variable declaration and then immediately using the variable exactly once, consider whether an anonymous object would be clearer.
Anonymous objects are inappropriate in several situations. If the object will be used more than once, a named variable is necessary and clearer. If the object represents an important conceptual entity in the code — a customer being processed, a configuration being applied — naming it documents its role and makes the code more readable. If debugging will be needed, named objects are easier to inspect in a debugger. If the object's creation can fail with an exception, separating the creation into a named variable makes error handling cleaner.
The most important case where anonymous objects cause problems is with mutable objects. If you pass a mutable anonymous object to a method that stores a reference to it, and then you need to modify it, you no longer have the reference — the object is inaccessible. This is not a problem for immutable value objects but is a significant problem for mutable objects with ongoing lifecycles like connections, timers, and registered listeners.
Java
// ── When anonymous objects are ideal: ────────────────────────────────
// GOOD: immutable value used once as argument:
double dist = origin.distanceTo(new Point(3.0, 4.0));
// GOOD: building a result to return immediately:
public Address getDefaultAddress() {
return new Address("Unknown", "Unknown", "N/A", "N/A");
}
// GOOD: test assertion with expected value:
assertThat(calculator.add(2, 3)).isEqualTo(5);
// GOOD: one-time use in a stream:
List<String> emails = users.stream()
.map(u -> new EmailBuilder(u.getEmail()).build())
.collect(toList());
// ── When named objects are better: ────────────────────────────────────
// BAD: object used multiple times — needs a name:
System.out.println(new Person("Alice", 30).getName());
System.out.println(new Person("Alice", 30).getAge()); // creates TWO objects!
// GOOD: name it, use it multiple times:
Person alice = new Person("Alice", 30);
System.out.println(alice.getName());
System.out.println(alice.getAge());
// BAD: mutable object that needs to be referenced later:
// new StringBuilder().append("start").append(moreData);
// Can't append more later — lost the reference!
// GOOD: name the mutable object:
StringBuilder sb = new StringBuilder();
sb.append("start");
if (condition) sb.append(" more");
sb.append(" end");
return sb.toString();
// BAD: complex construction that might throw — hard to debug anonymously:
processOrder(new Order(customerId, items, new Date(), discount, "PENDING"));
// If Order constructor throws, the stack trace shows the line but not
// which argument caused it.
// GOOD: named construction for debuggability:
Order order = new Order(customerId, items, new Date(), discount, "PENDING");
processOrder(order);Related 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.