第二个问题是如何读取应该是单例(Singleton)的序列化数据?当然要保证一是单例,二是数据正确性。Google 出来的所有文章都是一个调:private Object readResolve() { return getInstance(); }。方法没错,肯定要使用 readResolve() 返回一个唯一的实例。但真的可行吗?看下面的程序:
-
WrongSingletonObject.java
-
-
private static WrongSingletonObject instance;
-
-
-
private WrongSingletonObject() {
-
}
-
-
public static synchronized WrongSingletonObject getInstance() {
-
if (instance == null) {
-
instance = new WrongSingletonObject();
-
}
-
return instance;
-
}
-
-
this.value = value;
-
}
-
-
return value;
-
}
-
-
return getInstance();
-
}
-
}
-
-
public void singletonWrong(boolean save) {
-
if (save) {
-
try {
-
WrongSingletonObject singleton = WrongSingletonObject.getInstance();
-
singleton.setValue("value2");
-
-
out.writeObject(singleton);
-
-
out.close();
-
e.printStackTrace();
-
}
-
} else {
-
try {
-
-
WrongSingletonObject singleton = (WrongSingletonObject) in.readObject();
-
+ WrongSingletonObject.getInstance().getValue());
-
-
in.close();
-
e.printStackTrace();
-
}
-
}
-
}
老规矩,使用 ant run-singleton-wrong 来运行,结果会是什么呢?
-
run-class:
-
[echo] === Singleton: Wrong, Saving ===
-
[java] Original Singleton Value is value1
-
[java] New Singleton Value is value2
-
-
run-class:
-
[echo] === Singleton: Wrong, Loading ===
-
[java] Loaded Singleton Value is value1
-
[java] Singleton.getInstance() value is value1
我们在序列化之前将对象中的值设为 "value2",但逆序列化之后得到的值却是缺省的 "value1"。问题出在哪里呢?答案其实很简单,在 readResolve() 方法中,直接用 getInstance() 创建出一个新的对象并将其返回。那其中的值理所当然就是原来的 "value1"。
那又该如何解决?就是要把唯一的这个 instance 不是 new 出来,而是要直接赋值为逆序列化出来的对象。那这个逆序列化出来的对象在哪里呢?其实是“不识庐山真面目,只缘身在此山中”,如下:
-
SingletonObject.java
-
-
instance = this;
-
return getInstance();
-
}
逆序列化时调用的 readResolve()就是针对你序列化的对象本身的,也就是中其中的 this 就是我们想要的对象。就是[Java Serialization Specification] 逆序列化的第 12 步。结果如下:
-
run-class:
-
[echo] === Singleton: Correct, Saving ===
-
[java] Original Singleton Value is value1
-
[java] New Singleton Value is value2
-
-
run-class:
-
[echo] === Singleton: Correct, Loading ===
-
[java] Loaded Singleton Value is value2
-
[java] Singleton.getInstance() value is value2
这样,问题就解决了,跟我们预期的一样。
最后,其实还有一个重要的问题,就是对单例类逆序列化的时机。如果别的类在单例类逆序列化之前就得到了单例类的实例,并保留了一份引用,这样整个系统中就有两个实例,而非一个,就破坏了单例的意义。所以应该在任何类使用之前对其进行逆序列化。不过这已经属于逻辑问题,不属于技术问题。
所有的代码可以下载:serialization.tar.gz




你这篇文章不对呀,我试了下,没有加这句“instance
你这篇文章不对呀,我试了下,没有加这句“instance = this;“,结果是正确的呀,你的错了吧,怎么会有第一种结果出来?
你这篇文章不对呀,我试了下,没有加这句“instance
你这篇文章不对呀,我试了下,没有加这句“instance = this;“,结果是正确的呀,你的错了吧,怎么会有第一种结果出来?
你这篇错误!根本不需要加” instance =
你这篇错误!根本不需要加” instance = this;“,如下代码你自己试试
你的错误原因
你的错误原因就在于你在同一个 JVM 实例中进行序列化与反序列化,SingletonObject 本来指向的就是同一个,当然结果是一样的。
如果你将上面
hello.singletonWrong(XXX);中的 XXX 分两次设置为true和false,并分两次运行,你就知道结果了。或者更简单一点,程序拷贝两份放在不同的目录中,一个设置为
true,另一个设置为false,先运行第一个,再运行第二个,再看看结果如何。不加这一句照样是正确的,你怎么解释?
不加这一句照样是正确的,你怎么解释?
Post new comment