8

我正在尝试通过添加 readResolve() 方法来编写一个可序列化的单例类。我的意图是在序列化时获得与其对象状态相同的对象。

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

import java.io.FileInputStream;
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() {
    ObjectOutputStream oos = null;

    try {
        oos = new ObjectOutputStream(new FileOutputStream("d:\\SingletonData.txt"));
        oos.writeObject(MySerializedSingleton.getInstance());
    } 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 (IOException | 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 + "]";
   }
}

输出:

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]

我知道当前场景将始终返回具有最新对象状态的同一个单例实例。

我尝试覆盖 writeObject() 和 readObject() (在上面的代码中注释)但没有得到想要的结果。IE

After Deserialization : Singleton [i=5]

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

如果我的概念有误,请纠正我并帮助我解决这个问题。

谢谢。

4

7 回答 7

14

这是如何实现的:

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();
    s.i = 5;

    ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
    ObjectOutputStream oos = new java.io.ObjectOutputStream(baos);
    oos.writeObject(getInstance());
    oos.close();

    s.i = 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
}

}

于 2013-06-30T05:44:11.137 回答
5

实现SerializableSingleton 的最佳方法是使用Enum

来自 Joshua Bloch 的 Effective Java:

这种方法在功能上等同于公共字段方法,只是它更简洁,免费提供序列化机制,并且即使面对复杂的序列化或反射攻击,也能提供对多次实例化的铁定保证。虽然这种方法具有尚未被广泛采用,单元素枚举类型是实现单例的最佳方式。

节省一些时间并使用Enum

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

于 2015-05-27T20:22:59.010 回答
4

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

证明:修改 main() 方法如下:

public static void main(String[] args) throws Throwable {

    Singleton s = Singleton.getInstance();
    s.i = 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();

    s.i = 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 序列化.Singleton@1690726

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

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

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

请将线程也标记为“Singleton”,以便吸引更广泛的受众。

谢谢。

于 2015-05-27T19:52:35.927 回答
3

试试这个

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

请注意, readResolve 不需要公开。

于 2013-06-30T06:05:34.707 回答
0

这应该可以解决问题(基于您最初的问题):

   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

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

于 2016-06-29T15:39:00.393 回答
0

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

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

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

于 2018-04-23T09:20:39.700 回答
0

我们也可以这样做,调用getInstance()里面的方法 readResolve()并将结果存储到某个类型Singleton的引用变量中,然后返回引用。确保方法的返回类型应该是Object类型,您可以提供任何访问修饰符。

private Object readResolve() {
    Singleton instance = getInstance();
    return instance;
}
于 2019-02-07T09:57:58.057 回答