Java 序列化:readObject() 与 readResolve()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1168348/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java serialization: readObject() vs. readResolve()
提问by Forage
The book Effective Javaand other sources provide a pretty good explanation on how and when to use the readObject() method when working with serializable Java classes. The readResolve() method, on the other hand, remains a bit of a mystery. Basically all documents I found either mention only one of the two or mention both only individually.
Effective Java和其他来源一书很好地解释了在使用可序列化的 Java 类时如何以及何时使用 readObject() 方法。另一方面, readResolve() 方法仍然有点神秘。基本上我发现的所有文件要么只提到两者之一,要么只单独提到两者。
Questions that remain unanswered are:
尚未回答的问题是:
- What is the difference between the two methods?
- When should which method be implemented?
- How should readResolve() be used, especially in terms of returning what?
- 这两种方法有什么区别?
- 什么时候应该实施哪种方法?
- 应该如何使用 readResolve() ,尤其是在返回什么方面?
I hope you can shed some light on this matter.
我希望你能对这个问题有所了解。
采纳答案by Michael Myers
readResolve
is used for replacingthe object read from the stream. The only use I've ever seen for this is enforcing singletons; when an object is read, replace it with the singleton instance. This ensures that nobody can create another instance by serializing and deserializing the singleton.
readResolve
用于替换从流中读取的对象。我见过的唯一用途是强制单身人士;读取对象时,将其替换为单例实例。这确保没有人可以通过序列化和反序列化单例来创建另一个实例。
回答by Tom Hawtin - tackline
Item 90, Effective Java, 3rd Ed covers readResolve
and writeReplace
for serial proxies - their main use. The examples do not write out readObject
and writeObject
methods because they are using default serialisation to read and write fields.
第 90 项,Effective Java,第 3 版涵盖readResolve
和writeReplace
串行代理——它们的主要用途。这些示例不写出readObject
和writeObject
方法,因为它们使用默认序列化来读取和写入字段。
readResolve
is called after readObject
has returned (conversely writeReplace
is called before writeObject
and probably on a different object). The object the method returns replaces this
object returned to the user of ObjectInputStream.readObject
and any further back references to the object in the stream. Both readResolve
and writeReplace
may return objects of the same or different types. Returning the same type is useful in some cases where fields must be final
and either backward compatibility is required or values must copied and/or validated.
readResolve
在readObject
返回之后调用(相反writeReplace
在之前调用writeObject
并且可能在不同的对象上调用)。方法返回的对象替换this
返回给用户ObjectInputStream.readObject
的对象以及对流中对象的任何进一步反向引用。双方readResolve
并writeReplace
可以返还相同或不同类型的对象。在某些情况下,返回相同的类型很有用,其中字段必须是,final
并且需要向后兼容或必须复制和/或验证值。
Use of readResolve
does not enforce the singleton property.
的使用readResolve
不强制执行单例属性。
回答by Kanagavelu Sugumar
回答by endless
readResolve can be used to change the data that is serialized through readObject method. For e.g. xstream API uses this feature to initialize some attributes that were not in the XML to be deserialized.
readResolve 可用于更改通过 readObject 方法序列化的数据。例如,xstream API 使用此功能来初始化一些不在要反序列化的 XML 中的属性。
回答by Pr0methean
readResolve is for when you may need to return an existing object, e.g. because you're checking for duplicate inputs that should be merged, or (e.g. in eventually-consistent distributed systems) because it's an update that may arrive before you're aware of any older versions.
readResolve 用于当您可能需要返回现有对象时,例如因为您正在检查应该合并的重复输入,或者(例如在最终一致的分布式系统中)因为它是一个可能在您意识到之前到达的更新任何旧版本。
回答by Ankush soni
The readResolve Method
readResolve 方法
For Serializable and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing the readResolve method, a class can directly control the types and instances of its own instances being deserialized. The method is defined as follows:
对于 Serializable 和 Externalizable 类,readResolve 方法允许类在返回给调用者之前替换/解析从流中读取的对象。通过实现 readResolve 方法,一个类可以直接控制自己被反序列化的实例的类型和实例。该方法定义如下:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
ANY-ACCESS-MODIFIER 对象 readResolve() 抛出 ObjectStreamException;
The readResolvemethod is called when ObjectInputStreamhas read an object from the stream and is preparing to return it to the caller. ObjectInputStreamchecks whether the class of the object defines the readResolve method. If the method is defined, the readResolve method is called to allow the object in the stream to designate the object to be returned. The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastExceptionwill be thrown when the type mismatch is discovered.
所述的readResolve当方法被调用ObjectInputStream的已读取来自流的对象,并准备将其返回给调用者。ObjectInputStream检查对象的类是否定义了 readResolve 方法。如果定义了该方法,则调用 readResolve 方法以允许流中的对象指定要返回的对象。返回的对象应该是与所有用途兼容的类型。如果不兼容,则在发现类型不匹配时将抛出ClassCastException。
For example, a Symbolclass could be created for which only a single instance of each symbol binding existed within a virtual machine. The readResolvemethod would be implemented to determine if that symbol was already defined and substitute the preexisting equivalent Symbol object to maintain the identity constraint. In this way the uniqueness of Symbol objects can be maintained across serialization.
例如,可以创建一个Symbol类,其中每个符号绑定的单个实例存在于虚拟机中。所述的readResolve方法将实施,以确定该符号已经定义,并替换先前存在的等效Symbol对象保持标识约束。通过这种方式,可以在序列化过程中保持 Symbol 对象的唯一性。
回答by hi.nitish
When serialization is used to convert an object so that it can be saved in file, we can trigger a method, readResolve(). The method is private and is kept in the same class whose object is being retrieved while deserialization.
It ensures that after the deserialization, what object is returned is the same as was serialised. That is, instanceSer.hashCode() == instanceDeSer.hashCode()
当序列化用于转换对象以便将其保存在文件中时,我们可以触发方法 readResolve()。该方法是私有的,并保存在反序列化时正在检索其对象的同一类中。它确保反序列化后返回的对象与序列化的对象相同。那是,instanceSer.hashCode() == instanceDeSer.hashCode()
readResolve() method is not a static method. After in.readObject()
is called while deserialisation it just makes sure that the returned object is the same as the one which was serialized as below while out.writeObject(instanceSer)
readResolve() 方法不是静态方法。在in.readObject()
反序列化时调用后,它只是确保返回的对象与如下序列化的对象相同,而out.writeObject(instanceSer)
..
ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file1.ser"));
out.writeObject(instanceSer);
out.close();
In this way, it also helps in singleton design patternimplementation, because every time same instance is returned.
这样,它也有助于单例设计模式的实现,因为每次都返回相同的实例。
public static ABCSingleton getInstance(){
return ABCSingleton.instance; //instance is static
}
回答by arjun kumar mehta
readObject() is an existing method in ObjectInputStreamclass.while reading object at the time of deserialization readObject method internally check whether the class object which is being deserialized having readResolve method or not if readResolve method exist then it will invoke readResolve method and return the same instance.
readObject() 是ObjectInputStream类中的一个现有方法。在反序列化时读取对象 readObject 方法在内部检查正在反序列化的类对象是否具有 readResolve 方法,如果存在 readResolve 方法,则它将调用 readResolve 方法并返回相同的实例。
So the intent of writing readResolve method is a good practice to achieve pure singleton design pattern where no one can get another instance by serializing/deserializing.
因此,编写 readResolve 方法的意图是实现纯单例设计模式的好做法,在这种模式下,没有人可以通过序列化/反序列化来获得另一个实例。
回答by Omkar
As already answered, readResolve
is an private method used in ObjectInputStream while deserializing an object. This is called just before actual instance is returned. In case of Singleton, here we can force return already existing singleton instance reference instead of deserialized instance reference.
Similary we have writeReplace
for ObjectOutputStream.
正如已经回答的那样,readResolve
是在反序列化对象时在 ObjectInputStream 中使用的私有方法。这在返回实际实例之前调用。在单例的情况下,这里我们可以强制返回已经存在的单例实例引用而不是反序列化的实例引用。writeReplace
与 ObjectOutputStream类似。
Example for readResolve
:
示例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);
}
}
}
}
Output:
输出:
Before serialization: com.ej.item3.SingletonWithSerializable@7852e922
After deserialization: com.ej.item3.SingletonWithSerializable@7852e922
回答by Turin
I know this question is really old and has an accepted answer, but as it pops up very high in google search I thought I'd weigh in because no provided answer covers the three cases I consider important - in my mind the primary use for these methods. Of course, all assume that there is actually a need for custom serialization format.
我知道这个问题真的很老,并且有一个公认的答案,但是当它在谷歌搜索中出现的频率很高时,我认为我会权衡一下,因为没有提供的答案涵盖我认为重要的三种情况——在我看来,这些问题的主要用途方法。当然,都假设实际上需要自定义序列化格式。
Take, for example collection classes. Default serialization of a linked list or a BST would result in a huge loss of space with very little performance gain comparing to just serializing the elements in order. This is even more true if a collection is a projection or a view - keeps a reference to a larger structure than it exposes by its public API.
以集合类为例。与仅按顺序序列化元素相比,链表或 BST 的默认序列化将导致巨大的空间损失,而性能提升却很少。如果集合是投影或视图,则更是如此 - 保留对比其公共 API 公开的更大结构的引用。
If the serialized object has immutable fields which need custom serialization, original solution of
writeObject/readObject
is insufficient, as the deserialized object is created beforereading the part of the stream written inwriteObject
. Take this minimal implementation of a linked list: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... }
如果序列化对象有不可变字段需要自定义序列化,原来的解决方案
writeObject/readObject
是不够的,因为反序列化对象是在读取流中写入的部分之前创建的writeObject
。以链表的这个最小实现为例: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... }
This structure can be serialized by recursively writing the head
field of every link, followed by a null
value. Deserializing such a format becomes however impossible: readObject
can't change the values of member fields (now fixed to null
). Here come
the writeReplace
/readResolve
pair:
这个结构可以通过递归写入head
每个链接的字段,后跟一个null
值来序列化。然而,反序列化这种格式变得不可能:readObject
无法更改成员字段的值(现在固定为null
)。来了writeReplace
/readResolve
对:
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;
}
}
}
I am sorry if the above example doesn't compile (or work), but hopefully it is sufficient to illustrate my point. If you think this is a very far fetched example please remember that many functional languages run on the JVM and this approach becomes essential in their case.
如果上面的例子不能编译(或工作),我很抱歉,但希望它足以说明我的观点。如果您认为这是一个非常牵强的示例,请记住许多函数式语言在 JVM 上运行,并且这种方法在它们的情况下变得必不可少。
We may want to actually deserialize an object of a different class than we wrote to the
ObjectOutputStream
. This would be the case with views such as ajava.util.List
list implementation which exposes a slice from a longerArrayList
. Obviously, serializing the whole backing list is a bad idea and we should only write the elements from the viewed slice. Why stop at it however and have a useless level of indirection after deserialization? We could simply read the elements from the stream into anArrayList
and return it directly instead of wrapping it in our view class.Alternatively, having a similar delegate class dedicated to serialization may be a design choice. A good example would be reusing our serialization code. For example, if we have a builder class (similar to the StringBuilder for String), we can write a serialization delegate which serializes any collection by writing an empty builder to the stream, followed by collection size and elements returned by the colection's iterator. Deserialization would involve reading the builder, appending all subsequently read elements, and returning the result of final
build()
from the delegatesreadResolve
. In that case we would need to implement the serialization only in the root class of the collection hierarchy, and no additional code would be needed from current or future implementations, provided they implement abstractiterator()
andbuilder()
method (the latter for recreating the collection of the same type - which would be a very useful feature in itself). Another example would be having a class hierarchy which code we don't fully control - our base class(es) from a third party library could have any number of private fields we know nothing about and which may change from one version to another, breaking our serialized objects. In that case it would be safer to write the data and rebuild the object manually on deserialization.
我们可能希望实际反序列化与我们写入
ObjectOutputStream
. 这将是视图的情况,例如java.util.List
列表实现,它从更长的ArrayList
. 显然,序列化整个支持列表是一个坏主意,我们应该只写入查看切片中的元素。然而,为什么在反序列化后停止并具有无用的间接级别?我们可以简单地将流中的元素读入 anArrayList
并直接返回它,而不是将它包装在我们的视图类中。或者,拥有一个专用于序列化的类似委托类可能是一种设计选择。一个很好的例子是重用我们的序列化代码。例如,如果我们有一个构建器类(类似于 StringBuilder 的 StringBuilder),我们可以编写一个序列化委托,通过将一个空构建器写入流来序列化任何集合,然后是集合大小和集合的迭代器返回的元素。反序列化将涉及读取构建器,附加所有随后读取的元素,并
build()
从委托中返回 final 的结果readResolve
。在这种情况下,我们只需要在集合层次结构的根类中实现序列化,并且当前或未来的实现不需要额外的代码,只要它们实现抽象iterator()
和builder()
方法(后者用于重新创建相同类型的集合 - 这本身就是一个非常有用的功能)。另一个例子是有一个类层次结构,我们不能完全控制它的代码——我们来自第三方库的基类可能有任意数量的我们一无所知的私有字段,并且可能会从一个版本更改为另一个版本,破坏我们的序列化对象。在这种情况下,在反序列化时手动写入数据并重建对象会更安全。