Abstract: De-Serialization creates objects without calling constructors. We can use the same mechanism to create objects at will, without ever calling their constructors.
Welcome to the 175th issue of The Java(tm) Specialists' Newsletter. Something rather odd happened last night in Crete, which has not occurred since May. We had water falling down from the sky! So, it is time that I say goodbye to my extended summer holiday and start working again :-)
I am en route to the JavaZone conference in Oslo, Norway, where I will be speaking about some of the weird and wonderful things you can do with reflection (download slides here). Hope to see some of you at the conference! The organizers thought it would be fun to give me the second-last speaking slot of the last day ... at least if I embarrass myself, there won't be too many witnesses :-)
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
A few months before disappearing amongst the sand dunes of Chania's beaches on my extended summer vacation, I was explaining to the students on my advanced Java course how deserialization worked. If the object is serializable, then it is created magically without having the constructor called. If its parent class is not serializable, then the super class no-args constructor is invoked. For example, let's begin with a superclass that does not implement Serializable:
public class NotSerializable { public NotSerializable() { System.out.println("NotSerializable constructor called"); } }
We subclass this with MySerializable that prints out a message in the constructor:
import java.io.Serializable; public class MySerializable extends NotSerializable implements Serializable { public MySerializable() { System.out.println("MySerializable constructor called"); } }
We now write the MySerializable object to an ObjectOutputStream and generate a byte[] from which we then read back the object. When we run this, we see that the NotSerializable constructor is called twice, once when the MySerializable object is constructed and once when it is deserialized. We also see that the MySerializable constructor is only called once:
import java.io.*; public class DeserializeTest { public static void main(String[] args) throws IOException, ClassNotFoundException { MySerializable ms = new MySerializable(); // 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("reading ms2"); ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream(objectInBinaryForm) ); MySerializable ms2 = (MySerializable) 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:
NotSerializable constructor called MySerializable constructor called writing ms reading ms2 NotSerializable constructor called ms == ms2 = false ms = MySerializable@1827391d ms2 = MySerializable@6f7a29a1
By digging around a bit, I found the code that is used to construct the serializable object without calling the constructor, which we can use to create objects. To use it, we need to specify the first class in our hierarchy, where we want to call the actual constructor. For example, to simulate what happens in the DeserializeTest, we could call it with
SilentObjectCreator.create( MySerializable.class, NotSerializable.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); } } }
In our test class, we demonstrate what happens when we create the MySerializable by also calling the superclass constructor and the default behaviour of only calling Object's constructor:
public class CreationTest { public static void main(String[] args) { // Creating MySerializable by calling NotSerializable no-args // constructor, but not the MySerializable constructor. MySerializable ms = SilentObjectCreator.create( MySerializable.class, NotSerializable.class); System.out.println("ms = " + ms); // Creating MySerializable by not calling any constructors. MySerializable ms2 = SilentObjectCreator.create( MySerializable.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:
NotSerializable constructor called ms = MySerializable@1d5ee671 ms2 = MySerializable@593d93f4
We can use this mechanism to create just about any class, except abstract classes. For example, here we are making Thread.State.class instances:
import java.lang.reflect.Field; public class EnumCreation { public static void main(String[] args) throws Exception { Thread.State fastAsleep = SilentObjectCreator.create(Thread.State.class); Field name = Enum.class.getDeclaredField("name"); name.setAccessible(true); name.set(fastAsleep, "FAST_ASLEEP"); System.out.println("fastAsleep = " + fastAsleep); } }
This creates enum instances, but for a better approach, please see my newsletter 161.
This SilentObjectCreator could of course also be used to create Singleton instances, even if we were very careful to protect ourselves against that. For example:
public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { if (instance != null) { throw new IllegalStateException("already initialized"); } } public static Singleton getInstance() { return instance; } // etc. }
We can now easily make several instances of this, despite the check in the constructor, by simply instantiating it with the SilentObjectCreator class.
We should avoid using sun.* classes in our code directly, as it is then not portable and will break if we change JDKs. In my original article, I did not mention this (on purpose) as I assume that readers of an advanced Java newsletter would be aware of this.
Kind regards
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.