My notes on serialization and de-serialization

I wrote the following test to check some aspects of java serialization and de-serialization, followed by some notes:
package test;
import java.io.*;
import java.util.logging.Logger;

public class SerializableTest implements java.io.Serializable {

private static final long serialVersionUID = 0L;
private static final String fileName = "Serializable.ser";
private static final Logger logger = Logger.getLogger("");
private static String var;
transient private String tran = "this is a transient instance field";
private int number;
private Thread th;

public static void main(String[] args) throws IOException, ClassNotFoundException {
if (args[0].startsWith("ser")) {
SerializableTest test = new SerializableTest();
test.number = 1;
var = "this is a static variable";
writeOut(test);
System.out.println("SerializableTest to be saved: " + test);
} else if (args[0].startsWith("deser")) {
System.out.println("SerializableTest deserialized: " + readIn());
}
}

private static Object readIn() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File(fileName)));
return ois.readObject();
}

private static void writeOut(java.io.Serializable obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File(fileName)));
oos.writeObject(obj);
oos.close();
}

@Override public String toString() {
return "SerializableTest: final static fileName=" + fileName + ", final static logger=" + logger
+ ", non-final static var=" + var + ", instance number=" + number
+ ", transient instance tran=" + tran + ", non-serializable instance field:=" + th;
}
}
To run the program to serialize an instance to current directory:
$ java test.SerializableTest ser

SerializableTest to be saved: SerializableTest:
final static fileName=Serializable.ser,
final static logger=java.util.logging.LogManager$RootLogger@43ef9157,
non-final static var=this is a static variable,
instance number=1,
transient instance tran=this is a transient instance field
The output dumps the in-memory state of the object, before writing it to disk. To show the file type of the data file containing the object:
$ file Serializable.ser
Serializable.ser: Java serialization data, version 5
To de-serialize, or re-constitute the object from the data file:
$ java test.SerializableTest deser

SerializableTest deserialized:
SerializableTest: final static fileName=Serializable.ser,
final static logger=java.util.logging.LogManager$RootLogger@252f0999,
non-final static var=null,
instance number=1,
transient instance tran=null
The output shows the object state reconstructed from the data file.

(1) transient and static fields are ignored in serialization. After de-serialization transient fields and non-final static fields will be null. Final static fields still have values since they are part of the class data.

(2) ObjectOutputStream.writeObject(obj) and ObjectInputStream.readObject() are used in serialization and de-serialization, not BufferedOutputStream/BufferedInputStream. There is no BufferedObjectOutputStream or BufferedObjectInputStream.

(3) During serialization, you need to handle IOException; during de-serialization, you need to handle IOException and ClassNotFoundException. So the transitive closure of the de-serializaed type must be in the classpath on the receiving side.

(4) Some non-serializable classes: Thread, Logger, LogManager and most classes in java.util.logging (Level and LogRecord are serializable, though). Throwable extends Serializable, so all Error and Exception types are serializable via inheritance.

(5) Uninitialized non-serializable, non-transient instance fields are tolerated. When adding "private Thread th;", no error in serializable. However, "private Thread th = new Thread();" will cause exception:
Exception in thread "main" java.io.NotSerializableException: java.lang.Thread
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at test.SerializableTest.writeOut(SerializableTest.java:33)
at test.SerializableTest.main(SerializableTest.java:19)
(6) It is possible to include static fields in serialization and de-serialization, by implementing writeObject and readObject methods. These 2 methods are not in java.io.Serialiable, which is just a marker interface, and not in java.lang.Object, either. They are just 2 magic methods with pre-defined signatures. The following is from Technical Article: Advanced Serialization
private void writeObject(ObjectOutputStream oos) 
throws IOException {
oos.defaultWriteObject();
// Write/save additional fields
oos.writeObject(new java.util.Date());
}

// assumes "static java.util.Date aDate;" declared
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
ois.defaultReadObject();
// Read/initialize additional fields
aDate = (java.util.Date)ois.readObject();
}
For a definitive guide, refer to Official Java Object Serialization Specification version 6.0

(7) Serialization and de-serialization can be used for copying and cloning objects. It is slower than regular clone, but can produce a deep copy very easily. The following util method is adapted from Secrets of Java Serialization:
    public static Object clone(Object o) 
throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(o);
oos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
Object copy = ois.readObject();
ois.close();
return copy;
}

(8) If I need to serialize a Serializable class Person, but one of its superclasses is not Serializable, can Person class still be serializaed and de-serialized? The answer is yes, provided that the non-serializable superclass has a no-arg constructor, which is invoked at de-serialization to initialize that superclass.

(9) How to use a custom class loader during de-serialization? Oftentimes the context class loader of the receiving end is not the right one. Then you will need to create a subclass of java.io.ObjectInputStream, overriding resolveClass method:

protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException

Followers

Pageviews Last 7 Days