|
The Java Specialists' Newsletter
Issue 175 2009-09-08
Category:
Tips and Tricks
Java version: Java 6.0 Creating Objects Without Calling Constructorsby Dr. Heinz M. KabutzAbstract: 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 :-)
Thanks for reading this newsletter on our website. We also have a mailing list. That is where the real action takes place (webinars, free reports, etc.). Maybe subscribe today?
Advanced Java Courses on Crete:Java Specialists Master Course 18-21 June 2013 and
Concurrency Specialists Course 6-9 August 2013.
Creating Objects Without Calling Constructors
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.
Disclaimer
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
Tips and Tricks Articles
Related Java Course
Discuss at The Java Specialist Club
|