Serializable Singleton Instance的readResolve()方法的实现

我试图通过添加readResolve()方法编写Serializable Singleton类。 我的目的是在序列化时获得与对象状态相同的对象。

下面是我的测试示例代码:

import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class SingletonDemo { public static void main(String[] args) { Singleton obj = Singleton.getInstance(); System.out.println("After NEW Object creation : " + obj); obj.i = 5; System.out.println("Object modified"); System.out.println("After Object 1st Modification : " + obj); serializeMe(); System.out.println("Serialized successfully with object state : " + obj); obj.i = 10; System.out.println("Object modified again"); System.out.println("After Object 2nd Modification : " + obj); Singleton st = (Singleton)deSerializeMe(); System.out.println("Deserialized successfully"); System.out.println("After Deserialization : " + st); } public static void serializeMe() { FileOutputStream fos; ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("d:\\SingletonData.txt")); oos.writeObject(Singleton.getInstance()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static Object deSerializeMe() { ObjectInputStream oin = null; Object obj = null; try { oin = new ObjectInputStream(new FileInputStream("d:\\SingletonData.txt")); obj = oin.readObject(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return obj; } } class Singleton implements Serializable { int i; private static Singleton obj = null; private Singleton() { System.out.println("Executing constructor"); i=1; } public static Singleton getInstance() { if(obj == null) { obj = new Singleton(); } System.out.println("An instance is returned"); return obj; } /*private void writeObject(ObjectOutputStream oos) { try { oos.writeInt(i); } catch (Exception e) { e.printStackTrace(); } } private void readObject(ObjectInputStream ois) { try { i = ois.readInt(); } catch (Exception e) { e.printStackTrace(); } }*/ public Object readResolve() { System.out.println("Executing readResolve"); return Singleton.getInstance(); // FIXME } @Override public String toString() { return "Singleton [i=" + i + "]"; } } 

OUTPUT:

 Executing constructor An instance is returned After NEW Object creation : Singleton [i=1] Object modified After Object 1st Modification : Singleton [i=5] An instance is returned Serialized successfully with object state : Singleton [i=5] Object modified again After Object 2nd Modification : Singleton [i=10] Executing readResolve An instance is returned Deserialized successfully After Deserialization : Singleton [i=10] 

我知道当前场景将始终返回具有最新Object状态的Singleton的相同实例。

我尝试重写writeObject()和readObject()(在上面的代码中注释)但没有得到所需的结果。 即

 After Deserialization : Singleton [i=5] 

但是readResolve()中没有ObjectInputStream的引用,因此我可以在返回之前获取实例并使用序列化对象的状态更新它。

如果我的构思错了,请纠正我并帮助我解决这个问题。

谢谢。

以下是它的实现方式:

 public class Singleton implements Serializable { private static Singleton instance = new Singleton(); private int i; public static Singleton getInstance() { return instance; } private Singleton() { } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); instance = this; } private Object readResolve() { return instance; } public static void main(String[] args) throws Throwable { Singleton s = Singleton.getInstance(); si = 5; ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); ObjectOutputStream oos = new java.io.ObjectOutputStream(baos); oos.writeObject(getInstance()); oos.close(); si = 7; //modified after serialization InputStream is = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(is); Singleton deserialized = (Singleton) ois.readObject(); System.out.println(deserialized.i); // prints 5 } 

}

虽然有助于在反序列化对象上检索“i”的值,但是投票的结果是正确的,它违反了单例设计模式。 反序列化后,会创建两个“Singleton”类对象。

certificate:修改main()方法如下:

 public static void main(String[] args) throws Throwable { Singleton s = Singleton.getInstance(); si = 5; System.out.println("before serialization::"+s.i+" "+ s); //printing value and object ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(); ObjectOutputStream oos = new java.io.ObjectOutputStream(baos); oos.writeObject(getInstance()); oos.close(); si = 7; //modified after serialization System.out.println("modified after serialization::"+s.i+" "+s); InputStream is = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(is); Singleton deserialized = (Singleton) ois.readObject(); System.out.println("after deserialization::"+deserialized.i+" "+deserialized); //prints 5, but hashCode is different, which means object is not the same } 

输出是:

序列化之前:: 5 serialization.Singleton@1690726

序列化后修改:: 7 serialization.Singleton@1690726

反序列化后:5 serialization.Singleton@1662dc8

即使是第二个建议也存在同样的问题。 我尝试了更多配置,但没有成功。 有没有其他方法可以解决这个问题?

请用“Singleton”标记主题,以便更广泛的受众。

谢谢。

实现Serializable Singleton的最佳方法是使用Enum

来自Joshua Bloch的Effective Java:

这种方法在function上等同于公共领域方法,除了它更简洁, 免费提供序列化机制,并提供防止多个实例化的铁定保证,即使面对复杂的序列化或reflection攻击 。虽然这种方法有尚未被广泛采用,单元素枚举类型是实现单例的最佳方式。

节省一些时间并使用枚举

有关同一主题的更多讨论,请参阅此问题。

试试这个

  Object readResolve() { Singleton s = getInstance(); si = i; return s; } 

请注意,readResolve不需要公开。

这应该是诀窍(基于你的初步问题):

  public Object readResolve() { System.out.println("Executing readResolve"); if (obj == null) // optionally use external boolean flag to control this { System.out.println("readResolve - assigned obj = this - loaded state"); obj = this; } return Singleton.getInstance(); } 

如果要强制加载Singleton状态,则在反序列化存储状态之前设置obj = null

或者你可以添加一个boolean标志,告诉readResolve()方法是保留还是覆盖obj

如果您在multithreading环境中工作,请注意multithreading问题。

我相信简单地在子类中返回它应该可以解决问题

  public Object readResolve() { System.out.println("Executing readResolve"); return this; } 

我知道这是一个非常古老的post,但我偶然发现了它,其他人也可能。