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

San*_*ndy 6 java serialization deserialization

我试图通过添加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 + "]";
   }
}
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

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

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

After Deserialization : Singleton [i=5]
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢.

Jk1*_*Jk1 13

以下是它的实现方式:

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
}
Run Code Online (Sandbox Code Playgroud)

}

  • 上面的代码违反了 Singleton 属性,即如果你在最后打印 `s` 和 `s.getInstance()`,你会看到存在两个实例。错误在于方法“readObject”中对“instace”的重新定义。实际上,没有必要覆盖 `readObject`。@Dorofeev 给出的以下解决方案是正确的。 (2认同)

Bru*_*yne 5

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

来自Joshua Bloch的Effective Java:

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

节省一些时间并使用枚举.

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