Tuesday, August 22, 2006

Java Serialization vs .NET Serialization - Java Perverse?

Did you know what happens in Java when you serialize a subclass of a non serializable class? I was surprised by the answer: it works!

Unfortunately it is not a good thing, because it will serialize fields from your subclass and no fields from the parent class. So you'll end up with a half serialized instance.

In .NET, it breaks at runtime, throwing an exception, which is I think, much more logical, because then you don't end up with half data somewhere.

  • Java Code:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;



public class Test
{
  public static class Toto 
  {
    public String me;    
  }
  
  public static class Toto2 extends Toto implements Serializable
  {
    public String you;
    public String toString()
    {
      return me+" "+you;
    }
  }
  
  public static void main(String[] argsthrows Exception
  {
    Toto2 t = new Toto2();
    t.me = "it's me";
    t.you = "it's you";
    System.out.println("t="+t);
    ByteArrayOutputStream b = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(b);
    oos.writeObject(t);
    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(b.toByteArray()));
    System.out.println("u="+ois.readObject());
  }
}

will output:
t=it's me it's you
u=null it's you

  • .NET Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleApplication
{
    public class Toto
    {
        public string me;
        public override string ToString()
       {

            return me;
       }
    }
  
    [Serializable]
    public class Toto2 : Toto
    {
        public string you;
        public override string ToString()
       {

            return you + " " + me;
        }
    } 

    class Program
    {
        static void Main(string[] args)
        {

            Toto2 t = new Toto2();
            t.me =
"it's me";
            t.you =
"it's you";
            using (FileStream fs = File.Create(@"c:\test.bin"))
           {

                BinaryFormatter bFormatter = new BinaryFormatter();
                bFormatter.Serialize(fs, t);
            }
            Console.WriteLine("t=" + t.ToString());
            Toto2u = null;
            using (FileStream fs = File.Open(@"c:\test.bin", FileMode.Open))
            {

                BinaryFormatter bFormatter = new BinaryFormatter();
                u = (Toto2)bFormatter.Deserialize(fs);
            }
            Console.WriteLine("u="+u.ToString());
            Console.ReadKey();
        }
    }
}

will throw an exception.

3 comments :

  1. I think the Java behaviour is OK, because there may be superclasses which do not have a serializable state: abstract classes which just define behaviour or an interface or things like these. It is actually nessesary I think or you had to define an exception for the class Object, because it is not serializable!

    Furthermore you can define read/writeObject methods, so that you can store the state of the superclass evan if it is not serializable.

    ReplyDelete
  2. .Net serialization is a lot more restrictive too. As far as I know, .Net serialization only serializes public data, and it only works with classes that have default constructors. Java gets into the objects and gets all the private data and doesn't require the class to have a default constructor. I'll take Java's implementation any day.

    ReplyDelete
  3. That is not what happens in JAVA. You can in fact serialize a subclass of a non-serializable class, and regenerate it properly in a different JVM completely and not just partially. If your contention was true, JAVA would have never worked.
    From the javadoc for serlizable interface, " To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

    During deserialization, the fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream. "

    ReplyDelete