JAVA的序列化与反序列化(二)之源码分析

在上一篇文章中java的序列化与反序列化介绍到序列化是用来持久化对象的。只展示了基本的用法。本文章将介绍,为绕这几个问题展开:
1、为什么实现Serializable,而不做任何事情就可以进行序列化操作

2、如何自定义序列化的机制(比如:我们序列化一个对象的时候,需要自定义改变他的值)。

我们进入ObjectOutputStream源码的writeObject方法如下:

public final void writeObject(Object obj) throws IOException {
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                writeFatalException(ex);
            }
            throw ex;
        }
    }

在上述方法中并没有具体的操作,而是执行了writeObject0方法.主要代码如下:

	   private void writeObject0(Object obj, boolean unshared)
        throws IOException
        部分代码省略....
    {
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }

  部分代码省略....
}

上述代码进行四个判断:
1、如果是String类型 执行writeString
2、如果是数组集合类型 执行 writeArray(obj, desc, unshared);
3、如果是枚举类型执行 writeEnum((Enum) obj, desc, unshared);
4、如果实现了序列化接口 执行 writeOrdinaryObject(obj, desc, unshared);
否则:抛出异常NotSerializableException
看到这里,那么我们也就能回答文章 开头的第一个问题啦

1、为什么实现Serializable,而不做任何事情就可以进行序列化操作?

答:因为在序列化过程中会判断 是否实现序列化接口。序列化接口仅仅是个标识。
如果不满足上述四个条件中的一个。那么将会抛出异常NotSerializableException

接着看方法的调用writeOrdinaryObject 方法中又调用了writeSerialData方法。下面就直接给出writeSerialData方法的执行代码

  private void writeSerialData(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
        for (int i = 0; i < slots.length; i++) {
            ObjectStreamClass slotDesc = slots[i].desc;
            // 判断是有WriteObject方法
            if (slotDesc.hasWriteObjectMethod()) {
                PutFieldImpl oldPut = curPut;
                curPut = null;
                SerialCallbackContext oldContext = curContext;

                if (extendedDebugInfo) {
                    debugInfoStack.push(
                        "custom writeObject data (class \"" +
                        slotDesc.getName() + "\")");
                }
                try {
                    curContext = new SerialCallbackContext(obj, slotDesc);
                    bout.setBlockDataMode(true);
                    slotDesc.invokeWriteObject(obj, this);
                    bout.setBlockDataMode(false);
                    bout.writeByte(TC_ENDBLOCKDATA);
                } finally {
                    curContext.setUsed();
                    curContext = oldContext;
                    if (extendedDebugInfo) {
                        debugInfoStack.pop();
                    }
                }

                curPut = oldPut;
            } else {
                defaultWriteFields(obj, slotDesc);
            }
        }

上面的代码主要执行是:判断序列化的对象是否有WriteObject方法:
(1)如果有执行slotDesc.invokeWriteObject(obj, this);
(2)否则;执行defaultWriteFields(obj, slotDesc)

我们先看第一种情况如果序列化的对象hasWriteObjectMethod,执行invokeWriteObject方法;

  public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        MethodAccessor ma = methodAccessor;             // read volatile
        if (ma == null) {
            ma = acquireMethodAccessor();
        }
        return ma.invoke(obj, args);
    }

也就是利用反射调用WriteObject方法。
那么对于第二种情况;如果序列化对象的类没有WriteObject方法,那么就会执行默认的defaultWriteFields(obj, slotDesc);进行序列化操作。具体代码就不列出来了。

上面介绍的是序列化的时候,那么反序列化的底层原理又是如何呢?它的方法调用过程如下:
readObject()-------》readObject0(false)-----------》readOrdinaryObject()-------》readSerialData()----》部分代码如下:


            if (slots[i].hasData) {
                if (obj != null &&
                    slotDesc.hasReadObjectMethod() &&
                    handles.lookupException(passHandle) == null)
                {
                    SerialCallbackContext oldContext = curContext;

                    try {
                        curContext = new SerialCallbackContext(obj, slotDesc);

                        bin.setBlockDataMode(true);
                        slotDesc.invokeReadObject(obj, this);
                    } catch (ClassNotFoundException ex) {
                        /*

也是先判断hasReadObjectMethod(是否有ReadObjectMethod)。如果有那么调用定义的ReadObject方法。好了解读到这里。那是不是又可以回答文章开头的第二个问题

2、如何自定义序列化的机制(比如:我们序列化一个对象的时候,需要自定义改变他的值)。
答:我们可以在需要序列化的类中定义 readObject与writeObject。当序列化与反序列化的时候程序会尝试先调用他们。如果没类中没有定义那么,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法

基于上述分析。我们来给上一篇文章中定义的Product对象添加readObject与writeObject方法。代码如下:

    //自定义 方法
    private void writeObject(ObjectOutputStream s) throws IOException {
        s.writeObject("name");
        s.writeObject("describe");
    }

    private void  readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        name = "自定义更改name";
        describe = (String) s.readObject();
    }
输出结果:
序列化之前Product{name='电视机', price=3900.0, describe='这是一台高清'}
反序列化之后Product{name='自定义更改name', price=0.0, describe='name'}

这种让我们知道调用了自定义的readObject与writeObject方法。所以在序列化过程中我们就可以动态的改变值了。

总结:
1、序列化通过Serializable关键字声明
2、在类中增加writeObject 和 readObject 方法可以实现自定义序列化策略

你可能感兴趣的:(Java基础)