2020-03-16-Java的序列化

2020-03-23  本文已影响0人  耿望

序列化的应用场景有很多,比如网络传输是以字节流的方式对数据进行传输的,或者是将对象数据在进程之间进行传递,都需要进行序列化和反序列化。

Serializable

Serializable是Java接口,单从源码上看,它是一个空接口。实现了Serializable接口的类可以被ObjectOutputStream转换为字节流,同时也可以通过ObjectInputStream再将其解析为对象。

public interface Serializable {
}

不能被序列化的transient和static

因为序列化实际上是保持一个对象实例的状态,而静态变量是属于类的状态,所以static变量是不能序列化的。
同时,如果有一部分变量,是开发者不希望持久化的,可以声明为transient类型,这一类变量不会被持久化和反序列化。

ArrayList的序列化

实际上,ArrayList内部把elementData数组声明为transient类型,是不会被序列化的。但是它自己实现了writeObject和readObject方法。
因为ArrayList内部的数组有动态扩容机制,数组大小总是大于实际元素的个数,如果把整个elementData数组进行序列化,会导致开销过大,影响效率。

序列化和反序列化的过程

我们可以自己实现一个列子,通过堆栈打印来分析序列化和反序列化的过程。


序列化和反序列化.jpg

从堆栈中可以看到,最终ObjectStreamClass是通过反射来调用对象的writeObject方法。
反射的代码如下:

    /**
     * Returns non-static private method with given signature defined by given
     * class, or null if none found.  Access checks are disabled on the
     * returned method (if any).
     */
    private static Method getPrivateMethod(Class<?> cl, String name,
                                           Class<?>[] argTypes,
                                           Class<?> returnType)
    {
        try {
            Method meth = cl.getDeclaredMethod(name, argTypes);
            meth.setAccessible(true);
            int mods = meth.getModifiers();
            return ((meth.getReturnType() == returnType) &&
                    ((mods & Modifier.STATIC) == 0) &&
                    ((mods & Modifier.PRIVATE) != 0)) ? meth : null;
        } catch (NoSuchMethodException ex) {
            return null;
        }
    }

Externalizable

另外一种序列化的方式是Externalizable,它也是一个接口,继承了Serializable。

public interface Externalizable extends java.io.Serializable {
    void writeExternal(ObjectOutput out) throws IOException;
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

前面的Serializable方式,我们可以选择是否主动重写writeObject和readObject方法。
而Externalizable方式,必须重写writeExternal和readExternal方法。
从源码可以看出来,这两种序列化方式,前面几步都是一样的,在writeOrdinaryObject或readOrdinaryObject方法中,会根据对象的类型选择不一样的实现方式。

    private void writeOrdinaryObject(Object obj,
                                     ObjectStreamClass desc,
                                     boolean unshared)
        throws IOException
    {
            //……
            if (desc.isExternalizable() && !desc.isProxy()) {
                writeExternalData((Externalizable) obj);
            } else {
                writeSerialData(obj, desc);
            }
            //……
    }
    private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
       //……
        ObjectStreamClass desc = readClassDesc(false);
        desc.checkDeserialize();
       //……
        if (desc.isExternalizable()) {
            readExternalData((Externalizable) obj, desc);
        } else {
            readSerialData(obj, desc);
        }
       //……
    }

上面readOrdinaryObject方法中,会先检查对象是否支持反序列化,Externalizable方式有点特殊,它要求对象的类必须有一个无参构造函数,否则会抛出InvalidClassException异常。


序列化和反序列化 (2).jpg

总结一下,Externalizable的具体实现方式由开发者自己控制,Serializable方式可以交给Java实现,同时transient类型在Externalizable方式中没有意义。

參考

https://www.cnblogs.com/aoguren/p/4767309.html
https://www.jianshu.com/p/32a2ec8f35ae

上一篇下一篇

猜你喜欢

热点阅读