在java中反序列化单例

时间:2014-01-26 08:02:52

标签: java serialization

我正在阅读Effective Java,并且遇到了这个例子。

class Elvis implements Serializable {
    public static final Elvis  inst = new Elvis();
    private Elvis() {
        System.out.println("In elvis constructor ");
    }

    public static Elvis getInstance() {
        return inst;
    }
}

根据这本书,当我反序列化时,应该构造一个新的ELVIS对象,但是我看到在反序列化时没有调用构造函数?

这是我的代码序列化和反序列化。

FileOutputStream fos = new FileOutputStream("myserial1.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Elvis e = Elvis.getInstance();
System.out.println(" e = "+e.getInstance());

oos.writeObject(e);

System.out.println("Serialization done.");
FileInputStream fis = new FileInputStream("myserial1.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Elvis el = (Elvis) ois.readObject();
System.out.println(" el = "+el.getInstance());

我看到e和e1都引用相同的引用,构造函数只被调用一次。

我在这里误解了这个概念吗?

请帮忙。

4 个答案:

答案 0 :(得分:8)

在序列化期间,不会调用构造函数,通过反序列化过程或readObject() method初始化字段(如果将此方法添加到类中)。如果要实现可序列化的单例,则应另外添加readResolve()方法,如其描述的here


PS。
请注意,getInstance()是类Elvis的静态方法,因此e.getInstance()el.getInstance()等调用等于Elvis.getInstance()

答案 1 :(得分:1)

  

我在这里误解了这个概念吗?

您误解的是构造函数创建了一个对象。不,不。构造函数只是初始化对象。现在,反序列化不需要调用构造函数,因为它已经具有序列化对象的状态,这就是它必须为我们提供的。

但是,如果在可序列化类的层次结构中,有一些非可序列化的类,那么它的构造函数将被调用以初始化该类中的状态,因为它尚未被序列化。

您可以浏览Serialization Specification.

答案 2 :(得分:0)

According to the book, when i deserialize, a new ELVIS object should be constructed,
but i see the constructor is not called at the time of deserialization?

首先Object creation and constructor invocation are two separate thing。在正常情况下,使用new关键字创建对象时首先创建对象,然后调用构造函数。您可以看到创建对象的任何java类的字节码。

现在关于你的问题,在Serialization对象中创建的对象与任何其他对象一样,而不是运行构造函数值/使用reflection恢复状态。所以基本上从流中读取值(持久存储就是你的情况)并使用反射注入到对象中。

答案 3 :(得分:0)

由于Read和Write操作都在同一个jvm实例中执行 class已被加载用于读取操作,静态字段存储在类级别所以这里静态变量inst永远不需要重新实例化      public static final Elvis inst = new Elvis();

所以同样的引用是与类级别附加的返回