Making a copy of an object seems at first to be a straight forward
task:
Simply copy the values of all the properties into another
instance of the same class.
But what about the variables that are references to
other objects? Copies of these reference values mean they will point
to the same objects as the first class.
But maybe that is not what we want. Perhaps we want
all the objects referenced by the copy to be independent copies
as well.
These two types of object copies are called:
- shallow copy - exact bit copy of all the attributes of the original
object
- deep copy - primitives are copied exactly but objects referenced
are copied rather than the references themselves.
The Object
class, which is inherited by all Java classes, includes the clone()
method that will make exact bit copies of all the properties.
However, clone()
is a protected
method. So a given object can not be cloned by instances of any
classes outside the package (unless they are subclasses of that
object's class). This allows the class designer to specify explicitly
what kind of clones (shallow or deep) to make.
Java requires classes that want to override the clone()
method, to implement the cloneable
interface. The clone()
method must be made public
as well so as to override the access restrictions.
For example, the HashTable
class implements cloneable.
Its clone()
method makes a shallow copy so the keys and values of the copied
HashTable will reference the same objects as the original.
Many core Java classes, however, do not implement
cloneable.
If the clone()
method is invoked for such classes, a CloneNotSupportedException
will result.
Example.java |
import
java.util.*;
/** Illustrate cloning instances of a class. **/
public class Example implements Cloneable {
int fNum;
Integer [] fIarray;
Example (int nElements) {
fNum = nElements;
fIarray = new Integer[fNum];
// Set the array to random values
Random ran = new Random ();
for (int i=0; i < fNum; i++){
fIarray[i] = new Integer
(ran.nextInt (10000));
}
} // const
public Object clone () {
try {
return super.clone
();
}
catch (CloneNotSupportedException
e) {
throw new Error ("This
should never happen!");
}
} // clone
public static void main (String [] args)
{
Example ex = new
Example (5);
Example copy = (Example)ex.clone
();
// Change a value in the original
array.
ex.fIarray[0] = new Integer (1234567);
// Then the copy's value also
changes since it references
// the same array.
System.out.println ("Original
iarray[0] = " + ex.fIarray[0]);
System.out.println ("Copy
iarray[0] = " + copy.fIarray[0]);
} // main
} // Example
|
ExampleDeep.java
|
/** Illustrate
cloning a subclass of a cloneable class. **/
public class ExampleDeep extends Example {
ExampleDeep (int num) {
super (num);
} // const
public Object clone () {
// First make exact bitwise copy
ExampleDeep copy = (ExampleDeep)super.clone
();
// A new array of references to
Integer objects
// is created
copy.fIarray = new Integer[copy.fNum];
// Then the new Integer objects
given same internal
// values as the original
for (int i=0; i < copy.fNum; i++)
{
copy.fIarray[i] =
new
Integer (fIarray[i].intValue ());
}
return copy;
} // clone
public static void main (String [] args)
{
ExampleDeep ex = new
ExampleDeep (5);
ExampleDeep copy = (ExampleDeep)
ex.clone ();
// Change a value in the original
array.
ex.fIarray[0] = new Integer (1234567);
// But the copy's value
unchanged since it references
// a different array.
System.out.println ("Original iarray[0]
= " + ex.fIarray[0]);
System.out.println ("Copy
iarray[0] = " + copy.fIarray[0]);
} // main
} // ExampleDeep
|
For the Example
case:
c:\> java Example
Original iarray[0] = 1234567
Copy iarray[0] = 1234567
While for the DeepExample
case, a typical run goes as:
c:\> java Example
Original iarray[0] = 1234567
Copy iarray[0] = 1930
We see that the deep copy creates a separate array of Integer
objects.
Applet Cloning Demo
The following applet illustrates the same techniques
CloningJApplet8
Illustrates these same concepts in a graphical interface.
|
import
javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
/** Include Outputable interface if needed. **/
public class CloningJApplet8 extends JApplet
implements ActionListener
{
// A Swing fTextArea for display of string info
JTextArea fTextArea ;
// Flag for whether the applet is in a browser
// or running via the main () below.
boolean fInBrowser = true;
ShallowClass fShallowClass;
DeepClass fDeepClass;
public void init () {
// Create a User Interface with
a TextArea with sroll bars
// and a Go button to initiate processing
and a Clear button
// to clear the TextArea.
JPanel panel = new JPanel (new BorderLayout
());
// Create an instance of a fTextArea
fTextArea = new JTextArea ();
fTextArea.setEditable (false);
// Add to a scroll pane so that
a long list of
// computations can be seen.
JScrollPane area_scroll_pane = new
JScrollPane (fTextArea);
panel.add (area_scroll_pane,"Center");
JButton go_button = new JButton
("Shallow");
go_button.addActionListener (this);
JButton clear_button = new JButton
("Deep");
clear_button.addActionListener (this);
JButton exit_button = new JButton
("Exit");
exit_button.addActionListener (this);
JPanel control_panel = new JPanel
();
control_panel.add (go_button);
control_panel.add (clear_button);
control_panel.add (exit_button);
panel.add (control_panel,"South");
// Add text area with scrolling
to the content pane.
add (panel);
} // init
// Can use the start () method, which is called
after
// init () and the display has been created.
public void start () {
// Put task code here that will
generate text
// for the fTextArea.
} // start
public void actionPerformed (ActionEvent e)
{
String source = e.getActionCommand
();
if ( source.equals ("Shallow"))
shallowClone
();
else if ( source.equals ("Deep")
)
deepClone
();
else if (!fInBrowser)
System.exit
(0);
} // actionPerformed
public void shallowClone () {
ShallowClass copy;
fShallowClass = new ShallowClass
(5);
copy = (ShallowClass)fShallowClass.clone
();
// Modify original data
fShallowClass.setData (4, 1234567);
output ("Original = ", fShallowClass);
output ("Clone =
", copy);
} // shallowClone
public void deepClone () {
DeepClass copy;
fDeepClass = new DeepClass (5);
copy = (DeepClass)fDeepClass.clone
();
// Modify original data
fDeepClass.setData (4, 12345767);
output ("Original = ", fDeepClass);
output ("Clone =
", copy);
} // deepClone
public void output (String str, ShallowClass
obj) {
fTextArea.append (str);
Integer [] data = obj.getData ();
for (int i=0; i < 5; i++)
fTextArea.append (data[i]+"
");
fTextArea.append ("\n");
} // output
public static void main (String[] args) {
// *** Modify these values for the
particular program.
int frame_width=350;
int frame_height=350;
// *** Modify applet subclass name
to that of the program
CloningJApplet8 applet = new CloningJApplet8
();
applet.fInBrowser = false;
// Note that init () invoked before
adding to frame.
// So don't use width/height info
in init () since those
// parameters not yet set.
applet.init ();
// Following anonymous class used
to close window & exit program
JFrame f = new JFrame ("Frame &
Image Demo");
f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
// Add applet to the frame
f.getContentPane ().add (applet);
f.setSize (new Dimension (frame_width,frame_height));
f.setVisible (true);
}
} // main
class ShallowClass implements Cloneable {
Integer [] fData = null;
int fNumData = 5;
ShallowClass ()
{}
ShallowClass (int num) {
fNumData = num;
makeData ();
}
ShallowClass (Integer [] dat) {
fData = dat;
}
void makeData () {
Random ran = new Random ();
fData = new Integer[fNumData];
for (int i=0; i < fNumData; i++)
{
fData[i] = new Integer
(ran.nextInt (10000));
}
} // makeData
Integer [] getData () {
return fData;
}
void setData (int element, int val) {
if (element < fNumData)
fData[element] = new
Integer (val);
}
public Object clone (){
try {
// The clone's data
is same as the original
return (ShallowClass)super.clone
();
}
catch (CloneNotSupportedException
e ) {
throw new Error ("This
should never happen!");
}
}
} // ShallowClass
class DeepClass extends ShallowClass {
DeepClass ()
{ }
DeepClass (int num)
{ super (num); }
DeepClass (Integer [] dat)
{ super (dat);}
void setData (Integer [] dat)
{ fData = dat;}
public Object clone (){
// First make copy of the data
Integer [] clone_data = new Integer[fNumData];
for (int i=0; i < fNumData; i++)
{
// New Integer objects
but same values
clone_data[i] = new
Integer (fData[i].intValue () );
}
// Create a new instance of DeepClass
with
// the new data array.
DeepClass copy = (DeepClass)super.clone
();
copy.setData (clone_data);
return copy;
} // clone
} // DeepClass
|
Most recent update: Sept. 16, 2005
|