Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/370.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序列化:readObject()与readResolve()的比较_Java_Serialization_Singleton - Fatal编程技术网

Java序列化:readObject()与readResolve()的比较

Java序列化:readObject()与readResolve()的比较,java,serialization,singleton,Java,Serialization,Singleton,《有效的Java和其他源代码》一书很好地解释了在处理可序列化Java类时如何以及何时使用readObject()方法。另一方面,readResolve()方法仍然有点神秘。基本上,我找到的所有文件要么只提到这两个文件中的一个,要么只单独提到这两个文件 尚未回答的问题有: 这两种方法有什么区别 什么时候应该实施哪种方法 readResolve()应该如何使用,特别是在返回什么方面 我希望您能对此有所了解。readResolve用于替换从流中读取的对象。我所见过的唯一用途就是强制执行单例;读取对

《有效的Java和其他源代码》一书很好地解释了在处理可序列化Java类时如何以及何时使用readObject()方法。另一方面,readResolve()方法仍然有点神秘。基本上,我找到的所有文件要么只提到这两个文件中的一个,要么只单独提到这两个文件

尚未回答的问题有:

  • 这两种方法有什么区别
  • 什么时候应该实施哪种方法
  • readResolve()应该如何使用,特别是在返回什么方面

我希望您能对此有所了解。

readResolve
用于替换从流中读取的对象。我所见过的唯一用途就是强制执行单例;读取对象时,将其替换为singleton实例。这确保了没有人可以通过序列化和反序列化单例来创建另一个实例。

项目90,有效Java,第三版涵盖了串行代理的读取解析和写入替换-它们的主要用途。示例不写出
readObject
writeObject
方法,因为它们使用默认序列化来读取和写入字段


readResolve
readObject
返回后调用(相反,
writeReplace
writeObject
之前调用,可能在另一个对象上调用)。方法返回的对象将替换返回给
ObjectInputStream.readObject的用户的
对象以及流中对该对象的任何进一步反向引用。
readResolve
writeReplace
都可以返回相同或不同类型的对象。在某些情况下,返回相同类型很有用,因为字段必须是
final
,并且需要向后兼容,或者必须复制和/或验证值

使用
readResolve
不会强制执行singleton属性。

readResolve()将确保序列化时的singleton约定。

请使用readResolve更改通过readObject方法序列化的数据。例如,xstream API使用此功能初始化要反序列化的XML中未包含的某些属性


readResolve适用于您可能需要返回现有对象的情况,例如,因为您正在检查应合并的重复输入,或者(例如,在最终一致的分布式系统中)因为它是一个更新,可能在您意识到任何旧版本之前到达。

readResolve方法

对于可序列化和可外部化的类,readResolve方法允许类在将从流读取的对象返回给调用方之前替换/解析该对象。通过实现readResolve方法,类可以直接控制反序列化自身实例的类型和实例。该方法定义如下:

ANY-ACCESS-MODIFIER对象readResolve() 抛出异常

当ObjectInputStream从流中读取对象并准备将其返回给调用方时,将调用readResolve方法。ObjectInputStream检查对象的类是否定义readResolve方法。如果定义了该方法,则调用readResolve方法以允许流中的对象指定要返回的对象。返回的对象的类型应与所有用途兼容。如果不兼容,当发现类型不匹配时,将抛出ClassCastException


例如,可以创建一个Symbol类,该类在虚拟机中仅存在每个符号绑定的一个实例。readResolve方法将用于确定该符号是否已定义,并替换先前存在的等效符号对象以维护标识约束。通过这种方式,符号对象的唯一性可以在整个序列化过程中保持。

当使用序列化转换对象以便将其保存在文件中时,我们可以触发一个方法readResolve()。该方法是私有的,并且保存在反序列化时检索其对象的同一类中。 它确保在反序列化之后,返回的对象与序列化的对象相同。也就是说,
instanceSer.hashCode()==instanceDeSer.hashCode()

readResolve()方法不是静态方法。反序列化时调用
in.readObject()
后,只需确保返回的对象与在
out.writeObject(instanceSer)

通过这种方式,它也有助于单例设计模式的实现,因为每次都返回相同的实例

public static ABCSingleton getInstance(){
    return ABCSingleton.instance; //instance is static 
}
readObject()是ObjectInputStream类中的现有方法。在反序列化时读取对象时,readObject方法内部检查正在反序列化的类对象是否具有readResolve方法。如果存在readResolve方法,则它将调用readResolve方法并返回同一实例


因此,编写readResolve方法的目的是为了实现纯单例设计模式,在这种模式下,任何人都无法通过序列化/反序列化获得另一个实例。

如前所述,
readResolve
是在反序列化对象时ObjectInputStream中使用的私有方法。这是在返回实际实例之前调用的。对于单例,这里我们可以强制返回已经存在的单例实例引用,而不是反序列化的实例引用。 类似地,我们为ObjectOutputStream提供了
writeReplace

readResolve
示例:

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 SingletonWithSerializable implements Serializable {
private static final long serialVersionUID = 1L;

public static final SingletonWithSerializable INSTANCE = new SingletonWithSerializable();

private SingletonWithSerializable() {
    if (INSTANCE != null)
        throw new RuntimeException("Singleton instance already exists!");
}

private Object readResolve() {
    return INSTANCE;
}

public void leaveTheBuilding() {
    System.out.println("SingletonWithPublicFinalField.leaveTheBuilding() called...");
}

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    SingletonWithSerializable instance = SingletonWithSerializable.INSTANCE;

    System.out.println("Before serialization: " + instance);

    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
        out.writeObject(instance);
    }

    try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
        SingletonWithSerializable readObject = (SingletonWithSerializable) in.readObject();
        System.out.println("After deserialization: " + readObject);
    }

}
}

输出:

Before serialization: com.ej.item3.SingletonWithSerializable@7852e922
After deserialization: com.ej.item3.SingletonWithSerializable@7852e922

我知道这个问题很古老,有一个公认的答案,
Before serialization: com.ej.item3.SingletonWithSerializable@7852e922
After deserialization: com.ej.item3.SingletonWithSerializable@7852e922
public class List<E> extends Serializable {
    public final E head;
    public final List<E> tail;

    public List(E head, List<E> tail) {
        if (head==null)
            throw new IllegalArgumentException("null as a list element");
        this.head = head;
        this.tail = tail;
    }

    //methods follow...
}
private Object writeReplace() {
    return new Serializable() {
        private transient List<E> contents = List.this;

        private void writeObject(ObjectOutputStream oos) {
            List<E> list = contents;
            while (list!=null) {
                oos.writeObject(list.head);
                list = list.tail;
            }
            oos.writeObject(null);
        }

        private void readObject(ObjectInputStream ois) {
            List<E> tail = null;
            E head = ois.readObject();
            if (head!=null) {
                readObject(ois); //read the tail and assign it to this.contents
                this.contents = new List<>(head, this.contents)
            }                     
        }


        private Object readResolve() {
            return this.contents;
        }
    }
}