To make a singleton class that is implemented using either of the previous approaches serializable (Chapter 11), it is not sufficient merely to add imple- ments Serializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method (Item 77). Otherwise, each time a serialized instance is deserialized, a new instance will be created
Which leads to my question: why declare all instance fields transient ? i think readResolve is enough! my question is :why the author said we shuold declare all instance fields transient in singletons
package com.effective.test;
import java.io.Serializable;
public class NormalBean implements Serializable {
private static final long serialVersionUID = 1L;
private transient int id;
/**
* no matter declare transient or not...nothing is different!why author say that??
*/
private/* transient */String name;
private NormalBean(int id, String name) {
this.id = id;
this.name = name;
}
private static final NormalBean INSTANCE = new NormalBean(12345,"jack");
public static NormalBean getInstance() {
return INSTANCE;
}
/*
* The readResolve method is called when ObjectInputStream has read an
* object from the stream and is preparing to return it to the caller.
* ObjectInputStream checks whether the class of the object defines the
* readResolve method. If the method is defined, the readResolve method is
* called to allow the object in the stream to designate the object to be
* returned.
*
* And in Singleton case we are returning the same instance that was created
* while classloading and no new instances are returned. So singletonness is
* maintained.
*/
private Object readResolve() {
return INSTANCE;
}
@Override
public String toString() {
return "NormalBean [id=" + id + ", name=" + name + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
+ "]";
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class TestTransientPlusReadResolve {
NormalBean bean=NormalBean.getInstance() ;
public void writeAndRead() throws IOException {
ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream("writeAndRead.txt"));
System.out.println("this is the one "+bean );
outStream.writeObject(bean);
outStream.close();
try {
Thread.sleep(500l);
} catch (InterruptedException e) {
e.printStackTrace();
}
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("writeAndRead.txt"));
try {
NormalBean backBean = (NormalBean) inputStream.readObject();
System.out.println("this is the one "+backBean );
System.out.println("still the One?"+(backBean==bean));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally{
inputStream.close();
}
}
public static void main(String[] args) throws IOException {
new TestTransientPlusReadResolve().writeAndRead();
}
}
Output is:
this is the one NormalBean [id=12345, name=jack, getClass()=class com.effective.test.NormalBean, hashCode()=366712642]
this is the one NormalBean [id=12345, name=jack, getClass()=class com.effective.test.NormalBean, hashCode()=366712642]
still the One?true
I don't think any issue will highlight if you use it in a single JVM (the class will hold the reference for the static singleton instance, no matter how you serialize it). Perhaps restarting after the serialization and reading the file would help, but I would think that class loading should happen before the deserialization.
It also has a performance consideration. If you don't need to serialize the values of the fields, because they are the same for a singleton, why would you serialize it, then e.g. send it over the network, then do readResolve
just to ignore the values you received?