Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 可序列化Singleton实例的readResolve()方法的实现_Java_Serialization_Deserialization - Fatal编程技术网

Java 可序列化Singleton实例的readResolve()方法的实现

Java 可序列化Singleton实例的readResolve()方法的实现,java,serialization,deserialization,Java,Serialization,Deserialization,我试图通过添加readResolve()方法来编写一个可序列化的单例类。我的目的是在序列化时获取具有对象状态的相同对象 下面是我的测试示例代码: import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.

我试图通过添加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;
}
}

输出:

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]
我知道当前场景将始终返回具有最新对象状态的同一个Singleton实例

我尝试重写writeObject()和readObject()(在上面的代码中有注释),但没有得到想要的结果。 i、 e

但是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();
    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
}
}试试这个

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

请注意,readResolve不需要公开

投票通过的解决方案虽然有助于检索反序列化对象上的“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序列化。Singleton@1690726

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

即使是第二个建议也有同样的问题。 我尝试了更多的配置,但没有成功。 还有别的办法解决这个问题吗

请在帖子上加上“Singleton”的标签,以便更广泛的受众


谢谢。

实现可序列化的
单例的最佳方法是使用枚举

摘自Joshua Bloch的《有效的Java》:

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

节省一些时间并使用枚举

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

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

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

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


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

我认为,在子类中简单地返回这个就可以了

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

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

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

private Object readResolve() {
    Singleton instance = getInstance();
    return instance;
}

感谢Jk1。但我有一个疑问,在反序列化时执行readObject()和readResolve()的顺序是什么?一个小警告,我会抛出一个
assertionError()
在私有构造函数中,只是为了防止反射攻击或有人从类中意外实例化
Singleton
。上述代码违反了Singleton属性,即如果在末尾同时打印
s
s.getInstance()
您看到存在两个实例。错误是在方法
readObject
中重新定义了
instace
。实际上,重写
readObject
是不必要的。下面由@Dorofev给出的解决方案是正确的。回答不正确,因为存储在枚举中的数据没有序列化!原始用例将还原为旧的v无法使用枚举进行版本转换。
   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();
   }
   public Object readResolve() {
      System.out.println("Executing readResolve");
      return this; 
   }
private Object readResolve() {
    Singleton instance = getInstance();
    return instance;
}