Tuesday, September 6, 2011

Creating java objects without calling constructors

If the object is serializable, then it is created magically without having the constructor called. This means if a class implements Serializable interface then that class constructor wont be called while Deserialization process.

See below example for more details.


public class parent {
  public parent() {
    System.out.println("parent constructor called");

  }
}



import java.io.Serializable;

public class child extends parent implements Serializable {
   public child() {
      System.out.println("child constructor called");
   }
}



import java.io.*;
public class DeserializeTest {
  public static void main(String[] args) throws IOException, ClassNotFoundException {
    child ms = new child();

    // writing object to byte[]
    System.out.println("writing ms");
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(ms);
    oos.close();
    byte[] objectInBinaryForm = baos.toByteArray();


    // reading object from byte[]
    System.out.println("....Deserialization flow");
    System.out.println("reading ms2");
    ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(objectInBinaryForm) );
    child ms2 = (child) ois.readObject();
    System.out.println("ms == ms2 = " + (ms == ms2));
    System.out.println("ms = " + ms);
    System.out.println("ms2 = " + ms2);
  }
}


The output is the following, note the number of times that each constructor is called:


parent constructor called
child constructor called
writing ms
....Deserialization flow
reading ms2
parent constructor called
ms == ms2 = false
ms = mySerialization.child@1a626f
ms2 = mySerialization.child@95fd19

While deserialization child constructor is not called. but still we got object after deserialzation.

Can you guess how it is done?

Below code will explain you in detail.

For example, to simulate what happens in the DeserializeTest, we could call it with
      SilentObjectCreator.create(child.class, parent.class)
    
Here is my SilentObjectCreator, used to instantiate objects without invoking any constructors:


import sun.reflect.ReflectionFactory;
import java.lang.reflect.Constructor;

public class SilentObjectCreator {

  public static <T> T create(Class<T> clazz) {
    return create(clazz, Object.class);
  }


  public static <T> T create(Class<T> clazz,
                             Class<? super T> parent) {
    try {
      ReflectionFactory rf =  ReflectionFactory.getReflectionFactory();
      Constructor objDef = parent.getDeclaredConstructor();
      Constructor intConstr = rf.newConstructorForSerialization( clazz, objDef );
      return clazz.cast(intConstr.newInstance());
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new IllegalStateException("Cannot create object", e);
    }
  }
}



Lets test with our test class below


public class Creatiopublic class CreationTest {
   public static void main(String[] args) {
     // Creating child by calling parent no-args constructor, but not the child constructor.
    System.out.println("Creating child by calling parent no-args constructor, but not the child constructor");
    child ms = SilentObjectCreator.create( child.class, parent.class);
     System.out.println("ms = " + ms);

     // Creating child by not calling any constructors.
     System.out.println("Creating child by not calling any constructors");
     child ms2 = SilentObjectCreator.create( child.class );
     System.out.println("ms2 = " + ms2);
   }
}


In the output we see that in the first case, the NotSerializable constructor is called, but not in the second:

Creating child by calling parent no-args constructor, but not the child constructor
parent constructor called
ms = mySerialization.child@f9f9d8
Creating child by not calling any constructors
ms2 = mySerialization.child@112f614

We can use this mechanism to create just about any class, except abstract classes.

No comments:

Post a Comment