DEV Community

Mayur Bodhare
Mayur Bodhare

Posted on

Why Your Java Object Disappears After One Line

Early in my Java journey, I saw two lines of code that looked almost the same:

Dog d = new Dog("Bruno");
d.bark();
Enter fullscreen mode Exit fullscreen mode
new Dog("Bruno").bark();
Enter fullscreen mode Exit fullscreen mode

Both make a dog and tell it to bark. So why does the second one have a special name, "anonymous object", and why do some people avoid it?

The short answer is that the two lines create the same kind of thing but treat it very differently afterwards. In this post, we'll look at what each one means, what the JVM does in both cases, and how to choose between them. We'll also clear up a mix-up that trips up almost every beginner: anonymous objects are not the same as anonymous classes.

Unless I show a full class, the snippets go inside a main method.

A quick reminder: objects and references

When you write new Dog("Bruno"), Java creates an object on the heap. The object doesn't have a name. To talk to it later, you need a reference, which is like its address.

A reference variable is a variable that holds that address:

Dog d = new Dog("Bruno");
Enter fullscreen mode Exit fullscreen mode
   STACK                 HEAP
  +--------+          +------------------+
  | d     -|--------> | Dog: name=Bruno  |
  +--------+          +------------------+
Enter fullscreen mode Exit fullscreen mode

Here is a small Dog class we'll use for the whole post:

class Dog {
    String name;

    Dog(String name) {
        this.name = name;
        System.out.println("Dog created: " + name);
    }

    void bark() {
        System.out.println(name + " says Woof!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Reference object: an object with a name tag

A quick note on the words. "Reference object" isn't an official Java term. People use it for an object that's held by a reference variable. (Don't confuse it with the java.lang.ref package, which is a different, advanced topic.) You'll also just hear "named object".

Dog d = new Dog("Bruno");
d.bark();
d.bark();
Enter fullscreen mode Exit fullscreen mode

Output:

Dog created: Bruno
Bruno says Woof!
Bruno says Woof!
Enter fullscreen mode Exit fullscreen mode

Because we kept the reference d, we can:

  • call as many methods as we like on the same object,
  • read or change its fields later,
  • pass it to other methods,
  • store it in a list or compare it with another object.

The object lives as long as it can still be reached through some reference.

Anonymous object: an object with no name tag

An anonymous object is an object you create with new but don't store in any variable.

new Dog("Tiger").bark();
new Dog("Tiger").bark();
Enter fullscreen mode Exit fullscreen mode

Output:

Dog created: Tiger
Tiger says Woof!
Dog created: Tiger
Tiger says Woof!
Enter fullscreen mode Exit fullscreen mode

Look at the output closely. "Dog created" appears twice. Each new made a brand new object. We never talked to the same Tiger twice. After each line, our code had no way to reach that object again.

Reference object:   d ----------> [ Dog: Bruno ]     we can come back to it

Anonymous object:   (no name) --> [ Dog: Tiger ]     used once, then unreachable
Enter fullscreen mode Exit fullscreen mode

State is lost between calls

This is the clearest way to see the difference. Here's a counter:

class Counter {
    int count;

    int incrementAndGet() {
        count++;
        return count;
    }
}
Enter fullscreen mode Exit fullscreen mode

With a reference, the state builds up:

Counter c = new Counter();
System.out.println(c.incrementAndGet());
System.out.println(c.incrementAndGet());
Enter fullscreen mode Exit fullscreen mode

Output:

1
2
Enter fullscreen mode Exit fullscreen mode

With anonymous objects, each call starts from scratch:

System.out.println(new Counter().incrementAndGet());
System.out.println(new Counter().incrementAndGet());
Enter fullscreen mode Exit fullscreen mode

Output:

1
1
Enter fullscreen mode Exit fullscreen mode

Same code, different result, because the second version used two different Counter objects.

What happens behind the scenes

Here's the part I wish someone had told me earlier: the JVM treats both kinds of objects exactly the same. An anonymous object is still allocated on the heap, and its constructor still runs. The only difference is what your code does with the reference afterwards.

You can see this in the bytecode (try javap -c on your class). For Dog d = new Dog("Bruno"); d.bark();, it looks roughly like this:

new           Dog
dup
ldc           "Bruno"
invokespecial Dog.<init>
astore_1                    // store the reference in variable d
aload_1                     // load it back
invokevirtual Dog.bark
Enter fullscreen mode Exit fullscreen mode

For the anonymous version, new Dog("Bruno").bark();, it's roughly:

new           Dog
dup
ldc           "Bruno"
invokespecial Dog.<init>
invokevirtual Dog.bark      // use the reference right away
Enter fullscreen mode Exit fullscreen mode

(The exact lines vary a bit between Java versions.) Notice there's no astore. The reference is created, used immediately and then dropped. Nothing keeps it.

So an anonymous object isn't "lighter" or "faster". It's just an object whose reference you chose not to keep.

What about garbage collection?

Java frees objects that nobody can reach anymore. That's the garbage collector's job.

  • An anonymous object that's used on one line is unreachable right after that statement ends, so it becomes eligible for cleanup. (Eligible doesn't mean deleted instantly. The garbage collector decides when to run.)
  • A reference object stays alive as long as some reference points to it.
  • You can also make a named object lose its name by re-pointing the variable:
Dog d = new Dog("A");
d = new Dog("B");     // nobody points to "A" now
Enter fullscreen mode Exit fullscreen mode

Output:

Dog created: A
Dog created: B
Enter fullscreen mode Exit fullscreen mode

After the second line, the Dog named "A" is in the same situation as an anonymous object: alive on the heap, but unreachable.

One important twist: "anonymous" only means no variable name in your code. If you hand the object to someone who keeps it, it lives on:

class Student {
    String name;
    Student(String name) { this.name = name; }
}
Enter fullscreen mode Exit fullscreen mode
List<Student> students = new ArrayList<>();
students.add(new Student("Asha"));    // anonymous object
students.add(new Student("Ravi"));    // anonymous object

System.out.println(students.size());
System.out.println(students.get(0).name);
Enter fullscreen mode Exit fullscreen mode

Output:

2
Asha
Enter fullscreen mode Exit fullscreen mode

Here we created two anonymous objects, but the list keeps a reference to each, so they're safe and reachable. This is probably the most common use of anonymous objects in real code.

Where anonymous objects are useful

They fit when you need an object once and only once:

  • Passing to a method or collection: students.add(new Student("Asha"));
  • Returning from a method: return new Result(status, message);
  • A one-shot chain of calls:
String reversed = new StringBuilder("Java").reverse().toString();
System.out.println(reversed);
Enter fullscreen mode Exit fullscreen mode

Output:

avaJ
Enter fullscreen mode Exit fullscreen mode
  • Starting a thread:
new Thread(() -> System.out.println("Hello from a thread")).start();
Enter fullscreen mode Exit fullscreen mode

In each case, giving the object a name would add a line and a variable that you'd never use again.

Don't mix up: anonymous object vs anonymous class

This confusion is very common, so let's clear it up.

  • An anonymous object has a class with a name (like Dog) but no variable holding it.
  • An anonymous class is a class with no name, defined and used on the spot, usually to implement an interface.
Runnable r = new Runnable() {
    public void run() {
        System.out.println("Running");
    }
};
r.run();
Enter fullscreen mode Exit fullscreen mode

Output:

Running
Enter fullscreen mode Exit fullscreen mode

The object here has a reference (r), so it isn't an anonymous object. But the class that it's built from has no name, so it's an anonymous class. These are two different ideas that just share the word "anonymous".

You can combine them in one line, new Runnable() { ... }.run();, and then it's both. But keep the two ideas separate in your head:

  • Is there a variable holding the object? If no, it's an anonymous object.
  • Is the class itself unnamed? If yes, it's an anonymous class.

Which one should I use?

  • Need to use the object more than once? Use a reference variable. An anonymous object gives you a different object each time.
  • Need to remember its state, read a field later or pass it to several places? Use a reference variable.
  • Using it for exactly one call or one argument? An anonymous object is clean and short.
  • Debugging? A named variable is easier. You can inspect it, and the name tells readers what the object is for.
  • Creating an object just to call one method that doesn't use its fields? That's a hint the method probably should be static. Then you wouldn't need an object at all.

As a rule of thumb: if you find yourself wanting to name it, give it a name. If naming it feels like noise, go anonymous.

Common mistakes

  1. Expecting state to carry over. Calling new Counter().incrementAndGet() twice gives you two separate counters. If you want the count to build up, keep one reference.
  2. Calling methods on "the same" anonymous object. There's no such thing. Two new expressions always mean two objects.
  3. Mixing up anonymous object and anonymous class. One is about a missing variable, the other is about a missing class name.
  4. Thinking anonymous objects use less memory. They're ordinary heap objects. They just become unreachable sooner, if nobody else holds them.
  5. Creating anonymous objects inside a loop for no reason. Something like new Random().nextInt(10) on every round creates a new object each time. Create the Random once outside the loop and reuse it.
  6. Assuming an anonymous object is deleted immediately. It becomes eligible for garbage collection, and the JVM cleans up when it decides to. And if a list or another object keeps a reference, it stays alive.

Quick recap

  • An object lives on the heap, and a reference variable holds its address.
  • A reference object (named object) is stored in a variable, so you can use it again and again.
  • An anonymous object is created with new but not stored, so you can use it only at that spot.
  • The JVM treats both the same way: allocate on the heap, run the constructor. The only difference is whether you keep the reference.
  • An anonymous object becomes eligible for garbage collection once nothing can reach it, but passing it to a list or method may keep it alive.
  • Anonymous object means no variable. Anonymous class means no class name. They're different things.

Try it yourself

Here's a small practice idea: write the Counter class from this post and try three versions. First, call incrementAndGet() three times on one reference and note the output. Second, call it three times using new Counter() each time and compare. Third, add a Counter to a list as an anonymous object, then get it back and call incrementAndGet() on it. This will show you exactly when state survives and when it doesn't.

For a bonus experiment, run javap -c on your class, and compare the bytecode of the named and anonymous versions. Spotting the missing astore yourself is a great moment.

If this helped, or if you spotted something I should fix, tell me in the comments. I'm still learning too.

Top comments (0)