java - Singleton模式序列化问题?
PHP中文网
PHP中文网 2017-04-17 11:51:32
0
2
434

单例实现的一种方式:私有构造器,公有静态工厂方法

public class Singleton2 {
    private static final Singleton2 INSTANCE = new Singleton2(); // 私有静态final域

    /*
     * 私有构造器
     */
    private Singleton2() {
    }

    /*
     * 公有静态工厂方法
     */
    public static Singleton2 getInstance() {
        return INSTANCE;
    }
}
  • 为了使利用这种方法实现的Singleton类变成可序列化的,仅仅在申明中加上“implements
    Serializable”是不够的。为了维护并保证Singleton,必须声明所有的实例域都是transient,并提供一个readResolve()方法,否则,每次反序列化一个序列化的实例时都会创建一个新的实例。

这段话是effective java中的描述,我不理解的是为什么仅仅加上implements Serializable不行呢,反序列化一个序列化的实例时会创建一个新的实例?

--

补充问题:
关于这种单例模式有一种评论如下:
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载这时候初始化instance显然没有达到lazy loading的效果。
这里的其他方式还可以是什么?

PHP中文网
PHP中文网

认证0级讲师

全部回覆(2)
黄舟

1 為什麼只加上implements serializable不行
這種方式的單例模式建構的基礎是封裝性,如何實現單例呢?由於構造函數變成了私有,外部無法構造,只能透過Singleton2.getInstance()來獲取,這樣實作了單列,但是序列化時是這樣嗎?序列物件如何知道呼叫getInstance()來取得物件?我們來看看java底層序列化的實現,打開JDK中類別ObjectStreamClass(這個類別是負責序列化類別物件的)

 private static ObjectStreamField[] getSerialFields(Class<?> cl)
        throws InvalidClassException
    {
        ObjectStreamField[] fields;
        if (Serializable.class.isAssignableFrom(cl) &&
            !Externalizable.class.isAssignableFrom(cl) &&
            !Proxy.isProxyClass(cl) &&
            !cl.isInterface())
        {
            if ((fields = getDeclaredSerialFields(cl)) == null) {
                fields = getDefaultSerialFields(cl);
            }
            Arrays.sort(fields);
        } else {
            fields = NO_FIELDS;
        }
        return fields;
    }

再往下追蹤可以明顯看出他是用反射方式來序列化,用反射方式,不是調用getinstance(),當然單例就失效了
2 每次反序列化一個序列化的實例時都會建立一個新的實例
由於反序列化時也是用反射,所有當然每一個物件會創建一個新的實例

迷茫

補充一下吧, 這裡反序列化時, 會產生一個新的實例, 是因為可以在 反射中繞開 java裡訪問權限的限制.

http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible(boolean)

對應到java原始碼裡, jdk1.7.0.21, ObjectStreamClass.java:

private static Constructor getExternalizableConstructor(Class<?> cl) {
    //...
        Constructor cons = cl.getDeclaredConstructor((Class<?>[]) null);
        cons.setAccessible(true);
    //...
}

private static Constructor getSerializableConstructor(Class<?> cl) {
    //...
        Constructor cons = initCl.getDeclaredConstructor((Class<?>[]) null);
    //...
        cons = reflFactory.newConstructorForSerialization(cl, cons);
        cons.setAccessible(true);
        return cons;
    //...
}

這樣, 反序列化時, 即可直接呼叫到 singleton的 私有建構器 來產生一個新的物件實例.

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板