美文网首页
2020-03-16-Java的序列化

2020-03-16-Java的序列化

作者: 耿望 | 来源:发表于2020-03-23 12:37 被阅读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

    相关文章

      网友评论

          本文标题:2020-03-16-Java的序列化

          本文链接:https://www.haomeiwen.com/subject/huwfehtx.html