> The constructor is called during deserialization, maybe because of the
> assignment to the field singleton.
Did the singleton instance exist before you started deserialising ? I.e. was
the class MySingleton already loaded ? If not then it'll be loaded as a result
of seeing that class mentioned in the data, and the singleton instance will be
constructed at that time, and so your print() statements would be misleading.
-- chris
>>> If I add code to the constructor, it executes during
>>> deserialization, so that doesn't work for me.
[quoted text clipped - 10 lines]
> is called during deserialization. If I use static final, the
> constructor is called during deserialization.
Perhaps you are seeing the construction during static (class)
initialisation. When an object is deserialised the constructor of the
most derived non-serialisable class (possibly Object) is invoked by the
serialisation mechanism.
"transient static" doesn't make any sense, as static (class) data is not
serialised.
>>> In the oreilly RMI book there is an example of a writeObject method,
>>> but it doesn't explain how you are supposed to represent fields of the
[quoted text clipped - 9 lines]
> once". I'm trying to find a way to have that behavior survive
> deserialization.
Are you trying to store the state of the object? That doesn't really
make any sense. You are making and reading a copy of the singleton
state, so there is more than one of the singletons in existence.
> The field "singleton" was important because witthout
> serialization/deserialization it represents the one and only instance
> of the object.(and the one and only time that the constructor was
> called).
That's a static, not an instance, field.
> Any scheme I can think of so far to indicate to the class that it is
> being constructed rather than deserialized is executed during
> deserializaion also, so I can't distinguish between the two.
readResolve is called after an object is constructed through
deserialisation.
> What about readObject/writeObject. I think I could get around the
> problem by implementing writeObject. How should a writeObject method
> represent the state of an object?
Not having writeObject is equivalent to having a writeObject that just
calls ObjectOutputStream.defaultWriteFields. It just writes the
non-transient field data out in a nice fashion. The only interesting
piece of data of a singleton to be serialised is its identity (i.e. its
class).
Tom Hawtin
Frank Fredstone - 17 Jan 2007 21:29 GMT
>>>> If I add code to the constructor, it executes during
>>>> deserialization, so that doesn't work for me.
[quoted text clipped - 15 lines]
> most derived non-serialisable class (possibly Object) is invoked by
> the serialisation mechanism.
So, you and Chris Uppal's post as well, are agreeing that the
constructor is called during deserialization.
>> For my purposes, the singleton means "cause a side-effect only
>> once". I'm trying to find a way to have that behavior survive
[quoted text clipped - 3 lines]
> make any sense. You are making and reading a copy of the singleton
> state, so there is more than one of the singletons in existence.
I want an object that has a state that is initialized only once, as a
result of an external side-effect. I also want it to be possible for
that state to be serialized, without the state being "reinitialized"
(in the sense of being intitialized again by a specific block of code)
during deserialization. I want the state (the field values) restored,
without the code that initializes them during construction being
called.
In any case, I found information about writeObject in:
http://java.sun.com/j2se/1.3/docs/guide/serialization/spec/examples.doc1.html
Based on that, I have this mutation of the java developer almanac
serializable singleton that seems to survive deserialization. The
external side-effect is represented by retrieving a system property.
The one instance of the class would be normally be referenced using:
MySingleton.instance();
If it were deserialized, the reference would be faked using:
(MySingleton) objectInputStream.readObject();
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamField;
import java.io.Serializable;
public class MySingleton implements Serializable {
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("instance", MySingleton.class),
new ObjectStreamField("value", String.class)
};
private static MySingleton instance = null;
private String value = null;
private MySingleton() {
System.out.println("Constructing MySingleton");
}
private MySingleton(boolean f) {
System.out.println("Constructing MySingleton f");
System.out.println("Setting value to " +
System.getProperty("abc"));
value = System.getProperty("abc");
}
public String value() { return value; }
public static MySingleton instance() {
if (instance == null) {
instance = new MySingleton(true);
}
return instance;
}
private void writeObject(ObjectOutputStream s) throws IOException {
ObjectOutputStream.PutField fields = s.putFields();
fields.put("instance", instance);
fields.put("value", value);
s.writeFields();
}
private void readObject(ObjectInputStream s) throws IOException {
try {
ObjectInputStream.GetField fields = s.readFields();
instance = (MySingleton) fields.get("instance", null);
value = (String) fields.get("value", null);
} catch (ClassNotFoundException cnfe) {
throw new IOException("" + cnfe);
}
}
}
Tom Hawtin - 17 Jan 2007 21:59 GMT
>> Perhaps you are seeing the construction during static (class)
>> initialisation. When an object is deserialised the constructor of the
[quoted text clipped - 3 lines]
> So, you and Chris Uppal's post as well, are agreeing that the
> constructor is called during deserialization.
The constructor is not called for the deserialised copy. It is called
for the singleton copy.
> I want an object that has a state that is initialized only once, as a
> result of an external side-effect. I also want it to be possible for
[quoted text clipped - 3 lines]
> without the code that initializes them during construction being
> called.
> private static MySingleton instance = null;
> public static MySingleton instance() {
> if (instance == null) {
> instance = new MySingleton(true);
> }
> return instance;
> }
This is not thread safe.
> private void readObject(ObjectInputStream s) throws IOException {
> try {
> ObjectInputStream.GetField fields = s.readFields();
> instance = (MySingleton) fields.get("instance", null);
But that doesn't change any current instance of MySingleton to the new
singleton. I suspect that a singleton is really not what you want (it
almost never is).
Anyway, proceeding on with deserialisation mutates the singleton, I
suggest just updating the actual singleton instance, and replacing
references to it in other deserialised object to the real version.
public class MySingleton implements java.io.Serializable {
private static final MySingleton INSTANCE = new MyInstance();
public static getInstance() {
return INSTANCE;
}
private String value;
private MySingleton() {
System.out.println("Constructing MySingleton");
}
public synchronized String getValue() {
return value;
}
public synchronized void setValue(String value) {
this.value = value;
}
private Object readResolve(java.io.ObjectInputStream s) {
// Copy data into real instance.
synchronized (INSTANCE) {
INSTANCE.value = this.value;
}
// Replace serial instance with the real one.
return INSTANCE;
}
}
Frank Fredstone - 17 Jan 2007 22:50 GMT
>>> Perhaps you are seeing the construction during static (class)
>>> initialisation. When an object is deserialised the constructor of the
[quoted text clipped - 6 lines]
> The constructor is not called for the deserialised copy. It is called
> for the singleton copy.
If I don't implement readObject/writeObject:
MySingleton ms = (MySingleton) objectInputStream.readObject();
System.out.println("After deserialization");
prints
Constructing MySingleton
After deserialization
then after:
ms.instance();
it does not print "Constructing MySingleton".
That makes me think that the constructor is being called when it's
deserialized.
I'm not sure if I understand the distinction you are making between
the deserialized copy and the singleton copy.
>> I want an object that has a state that is initialized only once, as a
>> result of an external side-effect. I also want it to be possible for
[quoted text clipped - 13 lines]
>> }
> This is not thread safe.
I should have used:
public static synchronized MySingleton instance() {
...
}
>> private void readObject(ObjectInputStream s) throws IOException {
>> try {
[quoted text clipped - 4 lines]
> singleton. I suspect that a singleton is really not what you want (it
> almost never is).
Is there a way to avoid the possibility of their being multiple copies
of an object due to deserialization?
I should also add a clone method that throws an exception
I'm treating serialization as some ugliness that allows you to violate
the interface of a class, and I want to do my best to make my class
survive that ugliness.
> Anyway, proceeding on with deserialisation mutates the singleton, I
> suggest just updating the actual singleton instance, and replacing
[quoted text clipped - 26 lines]
> }
> }
That class does not contain constant values as it's "state" and is not
initialized by a side-effect that must be used to inititialize the
state once and only once, which is what I need.
Tom Hawtin - 18 Jan 2007 14:34 GMT
> That makes me think that the constructor is being called when it's
> deserialized.
The constructor of a different object is called during deserialisation.
It doesn't make sense to deserialise a singleton. What you can do is
replace references to a singleton in a serial stream with the real
singleton instance.
> Is there a way to avoid the possibility of their being multiple copies
> of an object due to deserialization?
Implement readResolve to switch to another copy. (Although this can in
general be hacked to get at the original object.)
> I should also add a clone method that throws an exception
Object.clone is protected. So long as the class does not make clone
public, does not implement Cloneable and is unsubclassable, then there
is not a problem. Of course to usefully subclass the singleton, you'd
have to be able to create a copy of it anyway.
> I'm treating serialization as some ugliness that allows you to violate
> the interface of a class, and I want to do my best to make my class
> survive that ugliness.
Serialisation effectively provides a second interface[1] to your class.
It should be quite independent of the normal class' API.
Tom Hawtin
[1] interface as a generic term, not the Java keyword "interface".