Serialization


The ObjectOutputStream class is used for serializing objects into output stream. The serialized data can be store into local file and then later read by ObjectInputStream to deserialized it back to object instance. Serialization is often use as a way for saving application preferences and game state.

Classes must implement Serializable or Externalizable interfaces for ObjectOutputStream to serialize an instance of classes. ObjectOutputStream can serialize primitive types, classes and user defined classes. Following example show how to make object serializable by implementing Serializable interface.

package com.geek.tutorials.io.serializable;

import java.io.Serializable;

public class Class1 implements Serializable{ // Display Comment
This line will make Class1 serializable. You can serialize an instance of this class with ObjectOutputStream and then read it back with ObjectInputStream, which will be explain later in next code section.
	int value;
	
	public Class1(int value){
		this.value = value;
	}
	
	
	public int getValue(){
		return value;
	}
}
					
				

Following code show how you can serialize an serializable class to an ObjectOutputStream and store its state into local drive.

package com.geek.tutorials.io.serializable;

import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;

public class SerializableTest1 {
	
	public static void main(String[] args) {
		Class1 class1 = new Class1(5); // Display Comment
Declare an instance of Class1 that implemented serializable interface and initialized it value field to 5. The value is just for prove the state of the Class1 is being serialized into file.
			
		try{
			// Display Comment
Initialize instance of ObjectOutputStream with FileOutputStream that will output data into serialize1.ser file. Note that you may use any filename you prefer.
			FileOutputStream f = new FileOutputStream("serialize1.ser"); 
			ObjectOutputStream out = new ObjectOutputStream(f);
			
			out.writeObject(class1); //Display Comment
serialize class1 to ObjectOutputStream, then finish the operation with out.close() method.
			out.flush();
			out.close();
						
			FileInputStream f2 = new FileInputStream("serialize1.ser"); 
			ObjectInputStream in = new ObjectInputStream(f2);
			Class1 c1 =(Class1)in.readObject(); //Display Comment
You can use this to deserialize data from the serialized stream.
			in.close();
			
			System.out.println(c1.getValue()); //Display Comment
When you execute this example, you will get value '5' printed on console screen. This is the value pass into the constructor of Class1 before the serialization. It proved that the state of the Class1 instance had been serialized into serialized1.ser file.
			
		}catch(Exception e){
			System.out.println(e);
		}
	}	
}
				
				


Constructor

protected
ObjectOutputStream()

Constructs an instance of ObjectOutputStream.

Syntax
public ObjectOutputStream() throws IOException, SecurityException

Exception
IOException - Error occurs while writing to OutputStream.
SecurityException - if security manager denies subclassing.

Example
See Serialization Example

public
ObjectOutputStream(OutputStream out)

Constructs an instance of ObjectOutputStream that writes to OutputStream.

Syntax
public ObjectOutputStream(OutputStream out) throws IOException, SecurityException

Parameters
out - OutputStream to write to

Exception
IOException - Error occurs while writing to OutputStream.
SecurityException - throw if untrusted subclass try to overrides security-sensitive methods illegally.

Example
See Serialization Example

 


Method

protected void
annotateClass(Class cl)

Allow Subclasses to store additional class data in the stream

Syntax
protected void annotateClass(Class cl) throws IOException.

Parameters
cl - the class that currently writing by ObjectOutputStream.

Exception
IOException - Error occurs while writing additional class data.

Description
When writeObject() is call to serialize an object, a class descriptor of the object's class and handle of the object is written to the stream. The class descriptor is serialized only once per every objects of the same class in the output stream, in this way the object is serialized only once to the stream, any subsequence serialization of the same class of objects will only written its handle to the stream in form of object/handle pair. During the serialization of class descriptor, annotateClass() will be call, by default annotateClass() does nothing, subclasses of ObjectOutputStream is allow to overrides annotateClass to perform additional action during the data serialization.

Example
This example shows how we can overrides ObjectOutputStream.annotateClass() and ObjectInputStream.resolveClass() to customize the process of serialization. It allow us to force the serialization and deserialization perform additional action, or customize how the object instance being deserialize.

For example, if we has a serialized object that its class might outdate in the future and must be replace by latest implementation of the class, we can solve this problem by overriding ObjectOutputStream.annotateClass() to write class's version number into the output stream (see example code in MyOutputStream below), and during the deserialization we can overrides ObjectInputStream.resolveClass() and ObjectInputStream.resolveObject() to check the version number of the serialized object and upgrade it if necessary (see example code in MyInputStream below).

package com.geek.tutorials.io.ObjectOutputStream;

import java.io.*;

class MyOutputStream extends ObjectOutputStream{ 	
	float version;
	
	public MyOutputStream(OutputStream out) throws IOException{
		super(out);
	}
	
	protected void annotateClass(Class cl) throws IOException{ // Display Comment
Subclass of ObjectOutputStream that overrides annotateClass() method, it execute only once per all object of the same class until out.reset() is call. This code here write 'version' information to the output stream prior to serialization of the actual object.
		writeFloat(version);		
	}
	
	public void setVersion(float version){ // Display Comment
Set Object's version number, this method should be call before calling writeObject();
		
		this.version = version;
	}
}

class MyInputStream extends ObjectInputStream{
	
	float version;
	
	public MyInputStream(InputStream in)throws IOException, ClassNotFoundException{
		super(in);	
		enableResolveObject(true); // Display Comment
Enable overrided resolveObject(), our overrided resolveObject() will never execute without setting 'true' into this method.
	
	}
	
	protected Object resolveObject(Object obj){ // Display Comment
If version is not latest(e.g. 2.0), then upgrade the deserializing 1.0 class and return it as 2.0 class, else if it is latest class implementation, then do nothing and simply return it to readObject().
		if(version != 2.0f) 
			return new SimpleClass2(((SimpleClass1)obj).getValue());
		else			
			return obj;
	}
	
	protected Class resolveClass(ObjectStreamClass objClass) 
		throws IOException, ClassNotFoundException{ // Display Comment
We override this method to make it compatible with our "MyInputStream", it read version number from the serialized data for each new encountered class descriptor.
		this.version = readFloat();
		return super.resolveClass(objClass);
	}
}

// Display Comment
SimpleClass1 is example of old outdated class, while SimpleClass2 is latest class extended from SimpleClass1 where its getValue() method is overrides to add value by 100 upon returning result.
class SimpleClass1 implements Serializable{	
	static float version = 1.0f; 
	int value;	
	public SimpleClass1(int value){
		this.value = value;
	}
	
	public int getValue(){
		return value;
	}
}

class SimpleClass2 extends SimpleClass1{
	static float version = 2.0f;
	int value;
	public SimpleClass2(int value){
		super(0);
		this.value = value;
	}
	
	public int getValue(){
		return value+100;
	}
}


public class AnnotateClassExample {

	public static void main(String args[]){
		try{
			FileOutputStream f = new FileOutputStream("AnnotateClass.ser");
			MyOutputStream out = new MyOutputStream(f);
			
			SimpleClass1 simpleClass1 = new SimpleClass1(6); // Display Comment
This is where the example come to life. We first create an instance of SimpleClass1 and try calling its getValue() method just to show that it return an expected unchanged value of 6, then serialize this outdated instance of SimpleClass1 to the OutputStream, by setting its version number "1.0" and write the object to the stream.
			System.out.println("Before serialize simpleClass1 value:"+
				simpleClass1.getValue());
			out.setVersion(simpleClass1.version);
			out.writeObject(simpleClass1);
									
			SimpleClass2 simpleClass2 = new SimpleClass2(6); // Display Comment
Then we create an instance of SimpleClass2, output its value to console just to see it is overrided the method from SimpleClass1, which increase the original value by 100. Since we initialize the SimpleClass2 with value of 6, it suppose to display 106 to the console by calling getValue() method.
			System.out.println("Before serialize simpleClass2 value:"+
				simpleClass2.getValue());
			out.setVersion(simpleClass2.version);
			out.writeObject(simpleClass2);
			out.flush();
			out.close();
			
						
			FileInputStream f2 = new FileInputStream("AnnotateClass.ser");
			MyInputStream in = new MyInputStream(f2); // Display Comment
Now read back from the serialized data, note that the outdated SimpleClass1 are upgraded to its latest implementation - SimpleClass2. It is done internally in MyObjectInputStream.resolveObject() during deserialization. Even though we cast the first result of in.readObject() to SimpleClass1, but its structure is actually upgraded to SimpleClass2, you can see the result in the following console output. where both object now returning the same value, incrementing value by 100.
			SimpleClass1 in_simpleClass1 = (SimpleClass1)in.readObject();
			SimpleClass2 in_simpleClass2 = (SimpleClass2)in.readObject();
			in.close();
			
			System.out.println("After deserialized in_simpleClass1 value:"+
				in_simpleClass1.getValue());			
			System.out.println("After deserialized in_simpleClass2 value:"+
				in_simpleClass2.getValue()); // Display Comment
Execute this example and you will see the following console output:
Before serialize simpleClass1 value:6
Before serialize simpleClass2 value:106
After deserialized in_simpleClass1 value:106
After deserialized in_simpleClass2 value:106
				
				
		}catch(Exception e){
			System.out.println(e);
		}
	}	
}
						
public void
close()

Write remaining buffered to output stream and release any resources.

Syntax
public void close() throws IOException

Exception
IOException - If error occurs during write or release of the resources.

Description
It is call at the end of serialization process

Example
See Serialization Example

public void
defaultWriteObject()

Write non-static and non-transient fields of class to the stream.

Syntax
public void defaultWriteObject() throws IOException

Exception
IOException - If error occurs while writing or releasing resources.

Description
It can only be invoked from writeObject() method, and should be the first method that writes any data to the stream.

protected void
drain()

Write any buffered data to underlying output stream, but does not flush the stream.

Syntax
protected void drain() throws IOException

Exception
IOException - If error occurs while writing data to underlying output stream.

Description
It is similar to flush() method, but it only writing data to underlying output stream and does not flush the stream.

protected final boolean
enableReplaceObject(boolean enable)

By default, ObjectOutputStream does not allow replaceObject() to be overrides by subclass. This method must set to true if subclass of ObjectOutputStream is going to overrides replaceObject().

Syntax
protected final boolean enableReplaceObject(boolean enable) throws SecurityException

Exception
SecurityException - if security manager denies enabling the stream to overrides replaceObject().

Example
See Example in replaceObject()

public void
flush()

This method write any remaining buffered bytes to underlying stream and flush it.

Syntax
public void flush() throws IOException

Exception
IOException - If error occurs while writing data or flushing to underlying output stream.

Description
Writes out any buffered bytes to underlying output stream.

Example
See Serialization Example

protected Object
replaceObject(Object obj)

Replace an object with another object during serialization.

Syntax
protected Object replaceObject(Object obj) throws IOException

Parameter
obj - the object to be replaced, must be non-null.

Exception
IOException - Any kind of exception that might throws by the underlying OutputStream

Description
replaceObject() method replace obj with another object. The subclass that implement replaceObject() should enabled replacement of objects by invoke enableReplaceObject(true) in constructor.

package com.geek.tutorials.io.ObjectOutputStream;

import java.io.*;

class AutoUpgradeOutputStream extends ObjectOutputStream { 
	AutoUpgradeOutputStream(OutputStream out) throws IOException, StreamCorruptedException{
		super(out);
		enableReplaceObject(true); // Display Comment
AutoUpgradeOutputStream is subclass of ObjectOutputStream, enableReplaceObject() should set to true if we are overriding replaceObject() method, else nothing will happen in replaceObject();
	}
	
	protected Object replaceObject(Object obj){// Display Comment
If the obj are instance of OldClass, we upgrade it to NewClass and return the new object, else do nothing and just return the obj.
		if(obj instanceof OldClass){
			return new NewClass(((OldClass)obj).getValue());
		}
		return obj;
	}
}

class OldClass implements Serializable{	// Display Comment
An old serializable class that will be replace by new class during serialization.
	int value;	
	public OldClass(int value){
		this.value = value;
	}
	
	public int getValue(){
		return value;
	}
}

class NewClass extends OldClass{ // Display Comment
An upgraded class that use to replace an old class during serialization.
	int value;
	public NewClass(int value){
		super(0);
		this.value = value;
	}
	
	public int getValue(){
		return value+100; // Display Comment
The only different of new class is getValue() method return value that added by 100.
	}
}


public class ReplaceObjectExample {

	public static void main(String args[]){
		try{// Display Comment
Open a new AutoUpgradeOutputStream and serialize OldClass into OutputStream, the OldClass will be upgrade to NewClass during serialization. Then flush the buffered data out to stream and close it to complete the serialization.
		
			FileOutputStream f = new FileOutputStream("ReplaceObjectExample.ser");
			ObjectOutputStream out = new AutoUpgradeOutputStream(f);
			OldClass oldClass = new OldClass(20);
			out.writeObject(oldClass);
			out.flush();
			out.close();
			
			// Display Comment
Read back the serialized object with ObjectInputStream.readObject(). When the program use getValue() output data to console, you will see that the value of 20 has increase by 100 which is 120 now, it mean that the OldClass has successfully upgraded OldClass to NewClass with replaceObject during serialization.
			FileInputStream f2 = new FileInputStream("ReplaceObjectExample.ser");
			ObjectInput in = new ObjectInputStream(f2);
			NewClass updatedClass = (NewClass)in.readObject();
			
			System.out.println("value: "+updatedClass.getValue());
		}catch(Exception e){
			System.out.println(e);
		}
	}	
}
						
public void
reset()

Reset the state of the object output stream

Syntax
public void reset() throws IOException

Exception
IOException - Error occur if invoke reset() in the middle of serializing an object.

Description
During serialization, class descriptor (ObjectStreamClass) are recorded for each new occurrences of class. Any subsequence same class will not causes the ObjectOutputStream to serialize the class descriptor again. If you invoke reset() between serialization of objects, you can force the ObjectOutputStream to serialize class descriptor again even the same class descriptor is already serialized previously in the same output stream. Because annotateClass() will be invoke everytime after class descriptor serialized, this can be useful to reset the output stream if you are overriding annotateClass() and you want to make sure the annotateClass get invoke by the subclass of ObjectOutputStream everytime even if the same class of objects already serialized in the OutputStream previously.

package com.geek.tutorials.io.ObjectOutputStream;

import java.io.*;
import java.util.Date;

class TestAnnotateClassOutputStream extends ObjectOutputStream{
	
	public TestAnnotateClassOutputStream(OutputStream out) throws IOException{
		super(out);
	}
	
	protected void annotateClass(Class cl) throws IOException{// Display Comment
Write current timestamp during serialization. annotateClass() will only invoke if current object's class is first time writing into this stream, subsequence object of the same class will not causes this method to be invoke. However, you may force annotateClass to get invoke again by calling reset() before you serialize another object of same class into the output stream.
		writeLong((new Date()).getTime());		
	}
}

class TestAnnotateClassInputStream extends ObjectInputStream{
	
	public TestAnnotateClassInputStream(InputStream in)throws 
						IOException, StreamCorruptedException{
		super(in);		
	}
	
	protected Class resolveClass(ObjectStreamClass osc)throws
				IOException, ClassNotFoundException{// Display Comment
This method will only be invoke once for each type of object's class in the stream, any subsequence object of same class will not causes this method to be invoke agian. However, if the input stream encounter reset marker in the stream, it will invoke resolveClass() again for all the object of same class that had been loaded previously from the stream.
		System.out.println("Timestamp:"+readLong());
		return super.resolveClass(osc);
	}
	
}

class ResetTestClass implements Serializable{// Display Comment
This is just simple test class for this serialization example
	
	String text;
	
	public ResetTestClass(String text){
		this.text = text;
	}
	
	public String toString(){
		return text;
	}
}

public class ResetExample {

	public static void main(String args[]){
		try{
			FileOutputStream f = new FileOutputStream("ResetExample.ser");
			TestAnnotateClassOutputStream out = 
				new TestAnnotateClassOutputStream(f);
			
			// Display Comment
Serialize 3 ResetTestClass into the TestAnnotateClassOutputStream. TestAnnotateClassOutputStream will record timestamp during serialization, it only record timestamp for the object's class that are first time serialize in this OutputStream, however you can force the TestAnnotateClassOutputStream to record subsequence object of same class by calling reset() method before serializing next object.
			ResetTestClass simpleObj = new ResetTestClass("1st test string obj");
			ResetTestClass simpleObj2 = new ResetTestClass("2nd test string obj");
			ResetTestClass simpleObj3 = new ResetTestClass("3rd test string obj");
			
			out.writeObject(simpleObj);// Display Comment
For the first simpleObj being serialize, annotateClass() will be invoke internally in TestAnnotateClassOutputStream, which record the serialization timestamp in the output stream.
			out.reset();// Display Comment
However, if we call reset() method at this point. The class descriptor state will be clear and next object of the same class will causes annotateClass() to be invoke again, which record the timestamp again.
			out.writeObject(simpleObj2);
			out.writeObject(simpleObj3);// Display Comment
For simpleObj3 we did not call reset() method, just to show that without calling reset() method, annotateClass() does not get invoke agian. Run this example and you will see the following result output to console:

Timestamp:1183710134578
1st test string obj
Timestamp:1183710134593
2nd test string obj
3rd test string obj

The timestamp may vary depend on what time you running this example, note that before the output "2nd test string obj" and "3rd test string obj", there is no timestamp. This is because reset() never call at that point during serialization. Therefore annotateClass() never invoke at that point, and also during deserialization reset marker do not exist at that point and never causes resolveClass() to be invoke too.

			out.flush();
			out.close();
			
			FileInputStream f2 = new FileInputStream("ResetExample.ser");
			TestAnnotateClassInputStream in = new TestAnnotateClassInputStream(f2);
			Object obj = in.readObject();
			System.out.println(obj);
			
			obj = in.readObject();
			System.out.println(obj);
			
			obj = in.readObject();
			System.out.println(obj);
			
			in.close();
			
		}catch(Exception e){
			System.out.println(e);
		}
		
	}
	
}

						
public void
write()

This method write one or more bytes to underlying output stream.

Syntax
public void write(int oneByte) throws IOException
public void write(byte[] bytes) throws IOException
public void write(byte[] bytes, int offset, int count) throws IOException

Exception
IOException - If error occurs while writing data to underlying output stream.

public void
writeBoolean(boolean value)

Writes a boolean value to object output stream

Syntax
public void writeBoolean(boolean value) throws IOException

Parameter
value - The boolean value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeByte(int value)

Writes a byte value to object output stream

Syntax
public void writeByte(int value) throws IOException

Parameter
value - The byte value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeBytes(String str)

Writes string in sequence of bytes to object output stream

Syntax
public void writeBytes(String str) throws IOException

Parameter
str - The string value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeChar(int value)

Writes char to object output stream

Syntax
public void writeChar(int value) throws IOException

Parameter
value - The char value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeChars(String str)

Writes string in sequence of chars to object output stream

Syntax
public void writeChars(String str) throws IOException

Parameter
str - The string value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeDouble(double value)

Writes double to object output stream

Syntax
public void writeDouble(double value) throws IOException

Parameter
value - The double value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeFloat(float value)

Writes float to object output stream

Syntax
public void writeFloat(float value) throws IOException

Parameter
value - The float value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeInt(int value)

Writes int to object output stream

Syntax
public void writeInt(int value) throws IOException

Parameter
value - The int value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public void
writeLong(long value)

Writes long to object output stream

Syntax
public void writeLong(long value) throws IOException

Parameter
value - The long value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

public final void
writeObject(Object obj)

Writes an object to object output stream

Syntax
public void writeObject(Object obj) throws IOException

Parameter
obj - The object to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serialization Example.

public void
writeShort(int value)

Writes short to object output stream

Syntax
public void writeLong(long value) throws IOException

Parameter
value - The short value to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.

Example
See Serializing and Desrializing Primitive Data Example.

protected void
writeStreamHeader()

Subclass can override this method to write additional header data into output stream

Syntax
protected void writeStreamHeader() throws IOException

Exception
IOException - If error occurs while writing data to underlying output stream.

public void
writeUTF(String str)

Writes a String as primitive data into output stream

Syntax
public void writeUTF(String str) throws IOException, UTFDataFormatException

Parameter
str - The non-null string to be written.

Exception
IOException - If error occurs while writing data to underlying output stream.
UTFDataFormatException - If the UTF string data exceed 65,535 bytes.

Example
See Serializing and Desrializing Primitive Data Example.


Serializing and Desrializing Primitive Data

package com.geek.tutorials.io.ObjectOutputStream;

import java.io.*;

import com.geek.tutorials.io.serializable.Class1;

public class WritePrimitiveExample {

	public static void main(String arg[]){
		try{
			FileOutputStream f = new FileOutputStream("WritePrimiteExample.ser");
			ObjectOutputStream out = new ObjectOutputStream(f);
			
			out.writeBoolean(true); // writes a boolean
			out.writeByte(2); // writes a byte
			out.writeBytes("34"); // writes a bytes
			out.writeChar('D'); // writes a char
			out.writeChars("56"); // writes a chars
			out.writeDouble(6.0); // writes a double
			out.writeFloat(7.0f); // writes a float
			out.writeInt(8); // writes a int
			out.writeLong(999999999); // writes a long
			out.writeShort(10); // writes a short
			out.flush();
			out.close();
						
			FileInputStream f2 = new FileInputStream("WritePrimiteExample.ser");
			ObjectInputStream in = new ObjectInputStream(f2);
			
			boolean boolValue = in.readBoolean(); // read a boolean
			byte byteValue = in.readByte(); // read a byte
			
			int bytesValue = in.readByte(); // read first byte from bytes
			int bytesValue2 = in.readByte(); // read second byte from bytes
			
			char charValue = in.readChar(); // read a char
			
			char chars1 = in.readChar(); // read first char from chars
			char chars2 = in.readChar(); // read second char from chars

			double doubleValue = in.readDouble(); // read a double
			float floatValue = in.readFloat(); // read a float
			int intValue = in.readInt(); // read a int
			long longValue = in.readLong(); // read a long
			short shortValue = in.readShort(); // read a short
			
			in.close();
			
		}catch(Exception e){
			System.out.println(e);
		}
	}
	
	
}