【单例深思】单例与序列化

来源:互联网 发布:mac终端编辑文件 编辑:程序博客网 时间:2024/06/06 04:20
在前面的文章中提到,序列化会破坏单例模式,下面用静态内部类的实现方式,说明序列化对单例的影响:

public class Singleton implements Serializable{
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

判断反序列化后的对象与原来的实例是否为同一个对象?

public class Test {
    public static void main(String[] argsthrows FileNotFoundException, IOException, ClassNotFoundException {
        // 序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("storageFile"));
        oos.writeObject(Singleton.getInstance());
        
        // 反序列化
        File file = new File("storageFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        
        //判断是否是同一个对象
        System.out.println(newInstance == Singleton.getInstance());
    }
}  
输出结果为:false

说明反序列化后的实例和之前序列化的实例不是同一个对象,这就可能导致JVM中出现多个实例的情况,这样就违背了单例模式的要求。怎么解决这个问题呢?

之前的文章简单提了一下,给单例实现添加readResolve()方法即可,修改后的代码如下所示:

public class Singleton implements Serializable{
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    
    private Object readResolve() {
        return SingletonHolder.INSTANCE;
    }
}

相比之前只有一处改动,就是添加readResolve()方法,在方法内返回了单例,重新运行一下上述的TEST类Main方法,返回结果为:true
结果表明,反序列化后的实例和之前被序列化的实例是同一个对象,通过这个方法确实可以解决序列化对单例模式的影响。
其他的实现方式也一样可以通过这个方法解决序列化问题。

下面我们深入了解一下readResolve()方法为什么能解决这个问题?
首先,我们需要明白在没有添加这个方法之前,为什么返回了一个新的对象实例?

简单地说,这个新对象是在反序列化过程中,通过反射调用无参数的构造方法创建的。
从源码角度看,使用ObjectInputStream进行反序列化时,会使用它的readObject()方法。

 public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        if (enableOverride) {
            return readObjectOverride();
        }
        // if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            Object obj = readObject0(false); // obj 是要返回的对象
            handles.markDependency(outerHandlepassHandle);
            ClassNotFoundException ex = handles.lookupException(passHandle);
            if (ex != null) {
                throw ex;
            }
            if (depth == 0) {
                vlist.doCallbacks();
            }
            return obj;
        } finally {
            passHandle = outerHandle;
            if (closed && depth == 0) {
                clear();
            }
        }
    }  

readObject()方法内部,通过调用readObject0()方法返回需要被返回的对象。

 private Object readObject0(boolean unsharedthrows IOException {
        boolean oldMode = bin.getBlockDataMode();
        if (oldMode) {
            int remain = bin.currentBlockRemaining();
            if (remain > 0) {
                throw new OptionalDataException(remain);
            } else if (defaultDataEnd) {
                /*
                 * Fix for 4360508: stream is currently at the end of a field
                 * value block written via default serialization; since there
                 * is no terminating TC_ENDBLOCKDATA tag, simulate
                 * end-of-custom-data behavior explicitly.
                 */
                throw new OptionalDataException(true);
            }
            bin.setBlockDataMode(false);
        }
        byte tc;
        while ((tc = bin.peekByte()) == TC_RESET) {
            bin.readByte();
            handleReset();
        }
        depth++;
        totalObjectRefs++;
        try {
            switch (tc) {
                case TC_NULL:
                    return readNull();
                case TC_REFERENCE:
                    return readHandle(unshared);
                case TC_CLASS:
                    return readClass(unshared);
                case TC_CLASSDESC:
                case TC_PROXYCLASSDESC:
                    return readClassDesc(unshared);
                case TC_STRING:
                case TC_LONGSTRING:
                    return checkResolve(readString(unshared));
                case TC_ARRAY:
                    return checkResolve(readArray(unshared));
                case TC_ENUM:
                    return checkResolve(readEnum(unshared));
                case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared)); // 处理对象序列化
                case TC_EXCEPTION:
                    IOException ex = readFatalException();
                    throw new WriteAbortedException("writing aborted"ex);
                case TC_BLOCKDATA:
                case TC_BLOCKDATALONG:
                    if (oldMode) {
                        bin.setBlockDataMode(true);
                        bin.peek();             // force header read
                        throw new OptionalDataException(
                            bin.currentBlockRemaining());
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected block data");
                    }
                case TC_ENDBLOCKDATA:
                    if (oldMode) {
                        throw new OptionalDataException(true);
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected end of block data");
                    }
                default:
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X"tc));
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }
    }  

对象的序列化处理在 checkResolve(readOrdinaryObject(unshared)); 方法中进行。

  private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {
            throw new InternalError();
        }
        ObjectStreamClass desc = readClassDesc(false);
        desc.checkDeserialize();
        Class<?> cl = desc.forClass();
        if (cl == String.class || cl == Class.class
                || cl == ObjectStreamClass.class) {
            throw new InvalidClassException("invalid class descriptor");
        }
        Object obj;
            
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null// 反射实例化代码
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
        passHandle = handles.assign(unshared ? unsharedMarker : obj);
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(passHandleresolveEx);
        }
        if (desc.isExternalizable()) {
            readExternalData((Externalizable) objdesc);
        } else {
            readSerialData(objdesc);
        }
        handles.finish(passHandle);
              // 添加了readResolve()方法后,就执行判断内的代码
        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
        {
            Object rep = desc.invokeReadResolve(obj); // 调用readResolve方法
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                // Filter the replacement object
                if (rep != null) {
                    if (rep.getClass().isArray()) {
                        filterCheck(rep.getClass(), Array.getLength(rep));
                    } else {
                        filterCheck(rep.getClass(), -1);
                    }
                }
                handles.setObject(passHandleobj = rep);
            }
        }
        return obj;
    }  
  obj = desc.isInstantiable() ? desc.newInstance() : null这行代码中,会先判断是否实现了serializable/externalizable,如果实现了,就采用反射的方法实例化一个对象。

在这个方法内还有一个 if 判断,if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod())
其中 desc.hasReadResolveMethod() 用于判断被序列化的类中是否包含 readResolve() 方法,如果包含就返回true,在这个判断逻辑内,会执行desc.invokeReadResolve(obj);方法,该方法会反射调用我们声明的 readResolve() 方法,我们在该方法内返回了序列化的对象,因此我们也就明白了readResolve() 方法的工作原理了。

这种约定式的使用方式在 Java 中很常见的,通过这些分析我们明白了序列化机制对单例的影响,以及通过什么样的方法解决了这些影响。








0 0