Some reader favorites:
EJB fundamentals and session beans
Create a scrollable virtual desktop in Swing
Web services test code generator
Klaus Berg has recently released a test-code generator for JUnit-based Web service clients. If you're developing Web services
using Axis2 and XMLBeans this wizard could turn your JUnit test client coding into a powerful code generation process. It
also has uses for those using GUI-based testing tools like soapUI.
| Memory Analysis in Eclipse |
| Enterprise AJAX - Transcend the Hype |
Page 2 of 3
Back in January of 1998, JavaWorld initiated its JavaBeans column by Mark Johnson with an article on serialization, "Do it the 'Nescafé' way -- with freeze-dried JavaBeans." To summarize, serialization is the ability to turn a graph of objects (including the degenerate case of a single object)
into an array of bytes that can be turned back into an equivalent graph of objects. An object is said to be serializable if
it or one of its ancestors implements java.io.Serializable or java.io.Externalizable. A serializable object can be serialized by passing it to the writeObject() method of an ObjectOutputStream object. This writes out the object's primitive data types, arrays, strings, and other object references. The writeObject() method is then called on the referred objects to serialize them as well. Further, each of these objects have their references and objects serialized; this process goes on and on until the entire graph is traversed and serialized. Does this
sound familiar? This functionality can be used to achieve a deep copy.
The steps for making a deep copy using serialization are:
I have written a class called ObjectCloner that implements steps two through five. The line marked "A" sets up a ByteArrayOutputStream which is used to create the ObjectOutputStream on line B. Line C is where the magic is done. The writeObject() method recursively traverses the object's graph, generates a new object in byte form, and sends it to the ByteArrayOutputStream. Line D ensures the whole object has been sent. The code on line E then creates a ByteArrayInputStream and populates it with the contents of the ByteArrayOutputStream. Line F instantiates an ObjectInputStream using the ByteArrayInputStream created on line E and the object is deserialized and returned to the calling method on line G. Here's the code:
import java.io.*;
import java.util.*;
import java.awt.*;
public class ObjectCloner
{
// so that nobody can accidentally create an ObjectCloner object
private ObjectCloner(){}
// returns a deep copy of an object
static public Object deepCopy(Object oldObj) throws Exception
{
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try
{
ByteArrayOutputStream bos =
new ByteArrayOutputStream(); // A
oos = new ObjectOutputStream(bos); // B
// serialize and pass the object
oos.writeObject(oldObj); // C
oos.flush(); // D
ByteArrayInputStream bin =
new ByteArrayInputStream(bos.toByteArray()); // E
ois = new ObjectInputStream(bin); // F
// return the new object
return ois.readObject(); // G
}
catch(Exception e)
{
System.out.println("Exception in ObjectCloner = " + e);
throw(e);
}
finally
{
oos.close();
ois.close();
}
}
}
All a developer with access to ObjectCloner is left to do before running this code is ensure that all classes in the object's graph are serializable. In most cases,
this should have been done already; if not, it ought to be relatively easy to do with access to the source code. Most of the
classes in the JDK are serializable; only the ones that are platform-dependent, such as FileDescriptor, are not. Also, any classes you get from a third-party vendor that are JavaBean-compliant are by definition serializable.
Of course, if you extend a class that is serializable, then the new class is also serializable. With all of these serializable
classes floating around, chances are that the only ones you may need to serialize are your own, and this is a piece of cake
compared to going through each class and overwriting clone() to do a deep copy.
An easy way to find out if you have any nonserializable classes in an object's graph is to assume that they are all serializable
and run ObjectCloner's deepCopy() method on it. If there is an object whose class is not serializable, then a java.io.NotSerializableException will be thrown, telling you which class caused the problem.
A quick implementation example is shown below. It creates a simple object, v1, which is a Vector that contains a Point. This object is then printed out to show its contents. The original object, v1, is then copied to a new object, vNew, which is printed to show that it contains the same value as v1. Next, the contents of v1 are changed, and finally both v1 and vNew are printed so that their values can be compared.
import java.util.*;
import java.awt.*;
public class Driver1
{
static public void main(String[] args)
{
try
{
// get the method from the command line
String meth;
if((args.length == 1) &&
((args[0].equals("deep")) || (args[0].equals("shallow"))))
{
meth = args[0];
}
else
{
System.out.println("Usage: java Driver1 [deep, shallow]");
return;
}
// create original object
Vector v1 = new Vector();
Point p1 = new Point(1,1);
v1.addElement(p1);
// see what it is
System.out.println("Original = " + v1);
Vector vNew = null;
if(meth.equals("deep"))
{
// deep copy
vNew = (Vector)(ObjectCloner.deepCopy(v1)); // A
}
else if(meth.equals("shallow"))
{
// shallow copy
vNew = (Vector)v1.clone(); // B
}
// verify it is the same
System.out.println("New = " + vNew);
// change the original object's contents
p1.x = 2;
p1.y = 2;
// see what is in each one now
System.out.println("Original = " + v1);
System.out.println("New = " + vNew);
}
catch(Exception e)
{
System.out.println("Exception in main = " + e);
}
}
}
To invoke the deep copy (line A), execute java.exe Driver1 deep. When the deep copy runs, we get the following printout:
Original = [java.awt.Point[x=1,y=1]] New = [java.awt.Point[x=1,y=1]] Original = [java.awt.Point[x=2,y=2]] New = [java.awt.Point[x=1,y=1]]
This shows that when the original Point, p1, was changed, the new Point created as a result of the deep copy remained unaffected, since the entire graph was copied. For comparison, invoke the shallow
copy (line B) by executing java.exe Driver1 shallow. When the shallow copy runs, we get the following printout: