First, we'll look at serialization of aggregate objects (not much of a feat, as you'll see). We've got a quick example of
how to implement the Externalizable interface (for you control freaks out there). Then, we'll discuss how to keep sensitive information from being serialized
at all. Finally, we'll finish up with some enlightening reader feedback on last month's column.
Last month, we saw that, for any object descended from java.lang.Object, you can make a class serializable simply by adding implements java.io.Serializable to the class definition, because class java.io.ObjectOutputStream knows how to serialize any class descended from java.lang.Object (which means any class at all).
But what if your object contains references to other objects or is composed of other objects? No problem! The serialization
mechanism automatically detects references to other objects. As long as the "sub-objects" are also serializable, ObjectOutputStream serializes them and includes them in the stream.
Let's look at a concrete example of this. In the following code example, we implement a TreeNode object. This object has internal fields of sToken_ (a string) and iType_ and iValue_ (integers). It also contains references to two other objects, tnLeft_ and tnRight_, which are references to the node's left and right subtrees. (This node class could be extended easily for use in an expression
evaluator.)
import java.io.*;
import java.lang.*;
// This is boring, but it gets the point across.
public class TreeNode
extends java.lang.Object
implements java.io.Serializable {
protected int iType_;
protected int iValue_;
protected String sToken_ = new String("");
protected TreeNode tnLeft_ = null;
protected TreeNode tnRight_ = null;
// Necessary to be a well-behaved bean.
public TreeNode()
{
iType_ = iValue_ = -1;
}
// Explicit constructor
public TreeNode(int iType, int iValue, String sToken,
TreeNode tnLeft, TreeNode tnRight)
{
iType_ = iType;
iValue_ = iValue;
sToken_ = sToken;
tnLeft_ = tnLeft;
tnRight_ = tnRight;
}
// Print me (indented) and all of my children
public void print(String sIndent)
{
System.out.println(sIndent + "type: " + iType_);
System.out.println(sIndent + "value: " + iValue_);
System.out.println(sIndent + "token: " + sToken_);
System.out.println(sIndent + "left:");
if (tnLeft_ != null) {
tnLeft_.print(sIndent + " ");
} else {
System.out.println(sIndent + " (null)");
}
System.out.println(sIndent + "right:");
if (tnRight_ != null) {
tnRight_.print(sIndent + " ");
} else {
System.out.println(sIndent + " (null)");
}
}
// Property accessors
public void setToken(String sToken) { sToken_ = sToken; }
public String getToken() { return sToken_; }
public void setType(int iType) { iType_ = iType; }
public int getType() { return iType_; }
public void setValue(int iValue) { iValue_ = iValue; }
public int getValue() { return iValue_; }
public void setLeft(TreeNode tnLeft) { tnLeft_ = tnLeft; }
public TreeNode getLeft() { return tnLeft_; }
public void setRight(TreeNode tnRight) { tnRight_ = tnRight; }
public TreeNode getRight() { return tnRight_; }
};
A TestNode is created with token, type, and value, and is connected to left and right branches at construction time. The property accessors
allow us to set and interrogate the properties, including the left and right branches. (The BeanBox won't show the branches
as properties, since there's no PropertyEditor for them. For more on the BeanBox, see "The BeanBox: Sun's JavaBeans test container.")
Our test class creates a recursive tree structure of TreeNodes and writes it to a file. Here's the source for the test class, followed by a diagram of the structure it creates and serializes:
001 import java.io.*;
002 import java.beans.*;
003 import TreeNode;
004
005 public class StreamDemo {
006
007 private static void Usage() throws java.io.IOException
008 {
009 System.out.println("Usage:\n\tStreamDemo w file\n\tStreamDemo r file");
010
011 IOException ex = new IOException("ERROR");
012 throw ex;
013 }
014
015 public static void main(String[] args)
016 {
017 System.out.println(args.length);
018
019 try {
020 if (args.length <= 0)
021 {
022 Usage();
023 }
024
025 String cmd = args[0];
026
027 if (cmd.compareTo("w") == 0)
028 {
029 if (args.length != 2)
030 {
031 Usage(); // Unix anyone?
032 }
033
034 TreeNode tnLL = new TreeNode(4, 12, "Left Left",
035 null, null);
036 TreeNode tnL = new TreeNode(2, 4, "Left", tnLL, null);
037 TreeNode tnR = new TreeNode(7, 9, "Right", null, null);
038 TreeNode tnRoot = new TreeNode(1, 2, "Root", tnL, tnR);
039
040 tnRoot.print("");
041 042 FileOutputStream f = new FileOutputStream(args[1]);
043 ObjectOutputStream s = new ObjectOutputStream(f);
044
045 s.writeObject(tnRoot);
046
047 s.flush();
048 }
049
050 else if (cmd.compareTo("r") == 0)
051 {
052 if (args.length != 2)
053 {
054 Usage();
055 }
056 057 FileInputStream f = new FileInputStream(args[1]);
058 ObjectInputStream s = new ObjectInputStream(f);
059
060 System.out.println("Reading TreeNode:");
061
062 TreeNode tnRoot = (TreeNode) s.readObject();
063
064 tnRoot.print("");
065 }
066
067 else if (cmd.compareTo("i") == 0)
068 {
069 if (args.length != 2)
070 {
071 Usage();
072 }
073 074 // Given a name, look for "name.ser"
075 Object theBean = Beans.instantiate(null, args[1]);
076 String sName = theBean.getClass().getName();
077
078 if ( sName.compareTo("TreeNode") == 0 )
079 {
080 TreeNode tn = (TreeNode)theBean;
081 tn.print("");
082 }
083 else
084 {
085 System.err.println("There was a bean in that file, " +
086 "but it was a " + sName);
087 }
088 }
089
090 else {
091 System.err.println("Unknown command " + cmd);
092 Usage();
093 }
094
095 }
096
097 catch (IOException ex) {
098 System.out.println("IO Exception:");
099 System.out.println(ex.getMessage());
100 ex.printStackTrace();
101 }
102 catch (ClassNotFoundException ex) {
103 System.out.println("ClassNotFound Exception:");
104 System.out.println(ex.getMessage());
105 ex.printStackTrace();
106 }
107 }
108 };
The tree created by this code looks like this:

Tree structure of TreeNodes code sample
The test program lets you exercise the TreeNode class in one of three ways. The code in red (lines 42-45) creates FileOutputStream f and then uses f to create an ObjectOutputStream, upon which we then invoke writeObject(). The serialization "machinery" inside the ObjectOutputStream analyzes the object that's passed to it and serializes to the stream any fields it finds. If the ObjectOutputStream finds any non-null object references inside the TreeNode, it then calls writeObject recursively to serialize those objects, as well. In our sample case, it finds tnLeft_ and tnRight_ in each TreeNode, and serializes them if they're non-null.
wonderful articleBy Anonymous on June 30, 2009, 1:37 pmeven this article was 10 years old, its wonderful then all article available in WEB!!!!!!
Reply | Read entire comment
View all comments