Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Object-oriented language basics, Part 5

The root of all classes

  • Print
  • Feedback

Page 3 of 6

Cloning

Now you will look at Object's clone() method. Cloning is the process of making exact duplicates of some entity. Java supports object cloning by way of clone(), which features the following signature:

protected Object clone() throws CloneNotSupportedException


The clone() method returns a reference to an object that exactly duplicates the object that calls clone(). During the cloning operation, clone() does not call a constructor method. As a result, clone() creates a duplicate object more quickly than new -- and a constructor call.

Because the clone() method has an Object return type, the object reference that clone() returns must cast to the object's actual type before assigning that reference to a variable of the object's type. For example, to clone an object from a hypothetical Employee class, you specify something like Employee e2 = (Employee) e.clone (); -- where the code fragment assumes e contains a reference to a previously created Employee object.

Sometimes, a superclass supports cloning, and you want to prevent cloning in its subclass. To prevent cloning in the subclass, override the superclass's clone() method (in the subclass) and have the overridden clone() method explicitly throw an object of type CloneNotSupportedException to the JVM. Because clone() can throw CloneNotSupportedException objects, a throws clause is included in clone()'s method signature. For example, suppose a class named Subclass subclasses a class named SuperClass. Furthermore, suppose that Subclass consists of the following code:

class Subclass extends SuperClass
{
   protected Object clone () throws CloneNotSupportedException
   {
      throw new CloneNotSupportedException ();
   }
}


If you attempt to execute Subclass sc1 = (Subclass) sc.clone (); -- where sc is a previously created Subclass object -- Subclass's clone() method throws a CloneNotSupportedException object.

Unlike most of Object's methods, clone() is a protected method. That means only code in those classes that are part of Object's package and code in Object's subclasses (regardless of package) can call clone(). The implication of making clone() protected: by default, objects can clone themselves but cannot clone other objects.

For your first real taste of cloning, check out CloneDemo1:

Listing 5. CloneDemo1.java

// CloneDemo1.java
class CloneDemo1 implements Cloneable
{
   int instanceField = 3;
   public static void main (String [] args) throws CloneNotSupportedException
   {
      CloneDemo1 cd1 = new CloneDemo1 ();
      CloneDemo1 x = (CloneDemo1) cd1.clone ();
      System.out.println (x.instanceField);
      AnotherClass ac = new AnotherClass ();
//      AnotherClass y = (AnotherClass) ac.clone ();
   }
}
class AnotherClass
{
   int anotherInstanceField = 2;
}


CloneDemo1 creates a duplicate of the object that cd1 references, and the duplicate object's reference assigns to x. Both objects, referenced by cd1 and x, contain an instance field named instanceField. Furthermore, the value stored in both fields is 3. The CloneDemo1 cast is necessary so that clone()'s returned object reference can assign to x.

Note three items in CloneDemo1. First, the throws CloneNotSupportedException clause attached to main()'s signature means you wish to ignore any CloneNotSupportedException objects thrown from clone(). Those exception objects are thrown in one of two situations -- either you overrode clone() and purposely threw the exception in a subclass (to prevent subclass objects from being cloned) or you called Object's clone() method from code in a class that does not also implement the Cloneable interface.

As you can probably guess, the second item to note is the implements Cloneable clause. Cloneable is an interface -- a concept I will discuss in next month's article. For now, keep in mind that an interface resembles a class, but can only declare method signatures and constants. If you examine the documentation on Cloneable, you discover that the interface is empty. You must specify implements Cloneable in the class declaration for those classes whose code calls Object's clone() method. Behind the scenes, Cloneable helps Object's clone() method identify those subclasses that must have their field values duplicated. (Object's clone() method throws a CloneNotSupportedException object if a class, whose code calls that method, does not implement Cloneable.)

The final item to note is the comment line in CloneDemo1. That line attempts to clone an AnotherClass object. I inserted that comment because the line of code won't compile. By default, you cannot clone an object from another class. To do so, you must first override clone(), as Listing 6 demonstrates:

Listing 6. CloneDemo2.java

// CloneDemo2.java
class CloneDemo2 implements Cloneable
{
   int instanceField = 3;
   public static void main (String [] args) throws CloneNotSupportedException
   {
      CloneDemo2 cd2 = new CloneDemo2 ();
      CloneDemo2 x = (CloneDemo2) cd2.clone ();
      System.out.println (x.instanceField);
      AnotherClass ac = new AnotherClass ();
      AnotherClass y = (AnotherClass) ac.clone ();
      System.out.println (y.anotherInstanceField);
   }
}
class AnotherClass implements Cloneable
{
   int anotherInstanceField = 2;
   protected Object clone () throws CloneNotSupportedException
   {
      return super.clone ();
   }
}


Cloning divides into two categories: shallow and deep. Object's clone() method performs shallow cloning, in which clone() duplicates the contents of instance fields but does not duplicate the objects referenced by instance reference fields. Listing 7 demonstrates shallow cloning:

Listing 7. CloneDemo3.java

// CloneDemo3.java
class Employee
{
   String name = "John Doe";
}
class CloneDemo3 implements Cloneable
{
   Employee e = new Employee ();
   public static void main (String [] args) throws CloneNotSupportedException
   {
      CloneDemo3 cd3 = new CloneDemo3 ();
      CloneDemo3 x = (CloneDemo3) cd3.clone ();
      System.out.println ("cd3.e == x.e: " + (cd3.e == x.e));
      System.out.println ("cd3.e.name = " + cd3.e.name);
      System.out.println ("x.e.name = " + x.e.name);
   }
}


If you run CloneDemo3, you will discover that cd3.e == x.e returns true. In other words, object reference variable e (in the object referenced by cd3) refers to the same object as object reference variable e (in the object referenced by e).

Shallow cloning is not always desirable. For example, when objects are stored in a data structure object, and the data structure object is cloned, should not each data structure object have its own copy of a stored object? To get around that limitation, Java allows you to perform deep cloning, in which objects are also duplicated. To understand deep cloning, look at Listing 8:

Listing 8. CloneDemo4.java

// CloneDemo4.java
class Employee
{
   String name = "John Doe";
}
class CloneDemo4 implements Cloneable
{
   Employee e = new Employee ();
   public static void main (String [] args) throws CloneNotSupportedException
   {
      CloneDemo4 cd4 = new CloneDemo4 ();
      CloneDemo4 x = (CloneDemo4) cd4.clone ();
      System.out.println ("cd4.e == x.e: " + (cd4.e == x.e));
      System.out.println ("cd4.e.name = " + cd4.e.name);
      System.out.println ("x.e.name = " + x.e.name);
   }
   protected Object clone () throws CloneNotSupportedException
   {
      System.out.println ("A");
      CloneDemo4 temp = (CloneDemo4) super.clone ();
      if (e != null)
      {
          temp.e = new Employee ();
          temp.e.name = new String ("John Doex");
      }
      return temp;
   }
}


CloneDemo4 produces two distinct Employee objects that in turn contain name fields referencing two distinct String objects. There are two items two note. First, System.out.println ("A"); proves that CloneDemo4's overridden clone() method is called. Second, I purposely changed John Doe to John Doex to emphasize that both name fields refer to two distinct String objects. However, to correct the code, I should change John Doex to John Doe, because the purpose of cloning is to provide an exact duplicate.

The deep cloning technique involves first performing a shallow copy of instance fields, which is what CloneDemo4 temp = (CloneDemo4) super.clone (); accomplishes. Then, reference type instance fields must be assigned references to new objects, and those objects must be populated with duplicate information. (The e != null test is important. After all, imagine what would happen if e contains null.)

While studying the clone() method, you might wonder why temp.e.name = new String ("John Doex"); appears instead of temp.e.name = e.name.clone ();. The reason: you cannot clone String objects. Instead, you must create a new String object.

Before leaving the topic of cloning, note that you can also clone arrays. Array cloning requires no special support (such as implementing Cloneable and dealing with CloneNotSupportedException) as shown in CloneDemo5 below:

Listing 9. CloneDemo5.java

// CloneDemo5.java
class CloneDemo5
{
   public static void main (String [] args)
   {
      String [] s =
      {
         "first",
         "second"
      };
      String [] t = (String []) s.clone ();
      for (int i = 0; i < t.length; i++)
           System.out.println (t [i]);
      System.out.println ("s == t: " + (s == t));
      System.out.println ("\n" + (s [0] == t [0]) + "\n");
      int [] m =
      {
         10, 20, 30
      };
      int [] n = (int []) m.clone ();
      for (int i = 0; i < n.length; i++)
           System.out.println (n [i]);
   }
}


If you run CloneDemo5, you receive the following output:

  • Print
  • Feedback

Resources