Java:序列化单例模式的实际用途是什么?

4
我们一直在了解如何使用'readResolve()'来确保在序列化Singleton时具有单一性。但是为什么要首先想要序列化Singleton的实际用例是什么?
编辑:请注意:问题是关于为什么要序列化Singleton,而不是安全地执行此操作的方法。
3个回答

4
最常见的情况是你有一个表示大型数据结构的对象(例如XML文档中节点树)。这个数据结构可能包含单例值(例如,表示树中特定属性数据类型的单例实例)。树中的许多不同节点可能指向相同的共享单例值。有许多情况下你可能需要序列化这样的数据结构,例如发送对象副本到网络上。在这种情况下,你还需要序列化单例值。但是,当你重新读入数据结构时,你想使用单例的现有值,而不是新创建的实例。这就是为什么你需要readResolve()

2
Joshua Bloch在他的书《Effective Java (第二版)》中建议使用枚举作为单例模式。枚举是由虚拟机创建的,因此很难(或不可能)创建第二个实例。
对于普通的单例模式,您总是可以“hack”系统,参见: 单例模式
Hot Spot:
Multithreading - A special care should be taken when singleton has to be used in a multithreading application.
Serialization - When Singletons are implementing Serializable interface they have to implement readResolve method in order to avoid having 2 different objects.
Classloaders - If the Singleton class is loaded by 2 different class loaders we'll have 2 different classes, one for each class loader. 
Global Access Point represented by the class name - The singleton instance is obtained using the class name. At the first view this is an easy way to access it, but it is not very flexible. If we need to replace the Sigleton class, all the references in the code should be changed accordinglly.

谢谢您的回答,但我的问题更多地是关于为什么要序列化单例而不是如何序列化。我会编辑问题以反映这一点。 - shrini1000
例如对于GWT、JPA、Hibernate等其他通过网络发送的框架。 - Andrzej Jozwik
一个具体的例子或链接会很有帮助。 :) - shrini1000
1
请查看此答案:https://dev59.com/2XA85IYBdhLWcg3wD_O8#2958908 - Andrzej Jozwik

1

该对象可能是单例的,但它可能是较大结构的一部分,而该结构并不完全知道它。例如,它可以实现一个接口,该接口可以有几个不同的实现(因此具有多个实例):

interface StringSink extends Serializable { void dump(String s); }

class MultiDumper implements Serializable {
    private final StringSink sink;
    public MultiDumper(StringSink sink){ this.sink = sink; }
    void doSomeStuff(Collection<String> strings){
        for (String s : strings) sink.dump(s);
    }
}

现在,假设我们想要一个将字符串转储到标准输出的StringSink。由于只有一个标准输出,因此我们可以将其设置为单例模式:
/** Beware: Not good for serializing! */
class StdoutStringSink {
    public static final StdoutStringSink INSTANCE = new StdoutStringSink();
    private StdoutStringSink(){}
    @Override
    public void dump(String s){ System.out.println(s); }
}

我们可以这样使用它:

MultiDumper dumper = new MultiDumper(StdoutStringSink.INSTANCE);

如果您对此转储进行序列化,然后再进行反序列化,那么您的程序中将会有两个StdoutStringSink实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接