Cloning is not about avoiding the use of new
operator but creating a new instance that has the same state (values of its member fields) as that of the object that's being cloned.
So, you may either use clone()
(that would internally be using new only) or create a new instance yourself (using a copy constructor or firing the setters explicitly post-construction) to mirror the state of the source object.
In your third example
clonedAnimal = (Animal) super.clone();
is actually invoking Object.clone()
which creates the new instance here.
Also, try removing the clonedAnimal.setXXX()
methods afterwords and it should still work since by default a shallow copy should have already been created by Object.clone()
. They probably left them there for ease of understanding.
EDIT : (in response to OP's comments)
When you clone()
you're basically delegating the new obj()
call to the original object itself.
Why? So, that it copies the state as well. But, this doesn't guarantee that new wasn't used down the line; just that the object's class exposed a clone()
implementation to take the burden of creating copies on itself.
Here's where things get interesting. Object.clone()
is a native method. It's been implemented within the JVM that does C++ stuff to allocate heap memory and all other jazz. But, it's practically the same thing that JVM does when you call the new operator yourself (except copying of the object state as well).
But, this isn't the important point. What's important is to understand when return super.clone()
would truly return an independent cloned object. The answer is mostly NO even for moderately complex objects because it does a shallow copy.
So, if your cache object is heavy on resources you would have to provide your own implementation for cloning of cache objects. To just give you an example: let's assume your cache had a LinkedHashMap
of recently accessed objects. If you rely on super.clone()
alone; your cloned cache object (by virtue of a shallow copy) would only have a copy of the reference to your Map which means if you purge one of your caches the other would also get purged.
Hence, when I say new is employed via delegation to clone()
; I'm referring to classes providing their own deep-copy implementations (almost always the case) for cloning. And, since they are doing it within the boundaries of Java their first step (barring Reflection API) is to instantiate a new object using the new operator.