自己实现序列化和反序列化的过程

自己实现序列化和反序列化的过程

这个问题是我在进行单例模式学习的过程中遇到的,
如果实现了序列化接口, 还要做什么来防止反序列化破坏单例?
答:实现readResolve方法,直接将对象返回。因为如果已经实现了readResolve方法,反系列化后就会直接使用readResolve的返回值

由此引出以下问题:

在进行系列化和反序列化时是如何找到并运行自己实现的writeObject()和readObject()方法的?

public class Test1 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        FileOutputStream fos = new FileOutputStream("D:\\out.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Person p = new Person();
        p.name = "LZF";
        p.age = 19;
        oos.writeObject(p);
        oos.close();


        Thread.sleep(1000);
        File f = new File("D:\\out.txt");
        FileInputStream inputStream = new FileInputStream(f);//创建文件字节输出流对象
        ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
        Person person = (Person)objectInputStream.readObject();
        System.out.println(person);
    }

}

class Person implements Serializable {
    private static final long serialVersionUID = 1386583756403881124L;
    String name;
    int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        System.out.println("My writeObject");
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        System.out.println("My readObject");
    }
}

上述实例代码中,Person实现了Serializable接口,并且内部实现了自己的writeObject和readObject方法。在实际执行过程中,主方法调用的这两个方法会走到我们自己实现的writeObject和readObject方法中,结果如下。为什么会走到我们自己实现的方法中?

My writeObject
My readObject
Person{name='null', age=0}

用writeObject举例,readObject是一样的过程。

进入到writeObject的源码,没有具体操作,会执行到writeObject0方法,接着进入

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;
    }
}
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

这里解释了为什么要实现Serializable接口,即使接口中没有任何方法。

Serializable接口只做标识使用,如果实现了此接口,就会走到对应的方法writeOrdinaryObject中去执行逻辑。

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

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

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;
        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);
        }
    }
}

我们先看第一种情况如果序列化的对象hasWriteObjectMethod,执行invokeWriteObject方法;也就是利用反射调用WriteObject方法。

void invokeWriteObject(Object obj, ObjectOutputStream out)
    throws IOException, UnsupportedOperationException
{
    requireInitialized();
    if (writeObjectMethod != null) {
        try {
            writeObjectMethod.invoke(obj, new Object[]{ out });
        } catch (InvocationTargetException ex) {
            Throwable th = ex.getTargetException();
            if (th instanceof IOException) {
                throw (IOException) th;
            } else {
                throwMiscException(th);
            }
        } catch (IllegalAccessException ex) {
            // should not occur, as access checks have been suppressed
            throw new InternalError(ex);
        }
    } else {
        throw new UnsupportedOperationException();
    }
}
  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方法,那么就会执行默认的defaultWriteFields(obj, slotDesc);进行序列化操作。具体代码就不列出来了。

还有一个疑问就是怎么判断序列化的对象是否有WriteObject方法,即hasWriteObjectMethod(),内部实际上就是看writeObjectMethod的取值,如果不为空表示有实现WriteObject方法。

private Method writeObjectMethod;
......
boolean hasWriteObjectMethod() {
 requireInitialized();
 return (writeObjectMethod != null);
}

ObjectStreamClass 类中的 writeObjectMethod 私有变量是在类加载时通过反射来赋值的。具体来说,这个变量存储了类中用于序列化对象的 writeObject() 方法的引用。

这个变量的赋值过程:

在writeObject0方法中,有一段,获取被序列化类的Class,然后会执行ObjectStreamClass.lookup方法

// check for replacement object
Object orig = obj;
Class<?> cl = obj.getClass();
ObjectStreamClass desc;
for (;;) {
 // REMIND: skip this check for strings/arrays?
 Class<?> repCl;
 desc = ObjectStreamClass.lookup(cl, true);
 if (!desc.hasWriteReplaceMethod() ||
     (obj = desc.invokeWriteReplace(obj)) == null ||
     (repCl = obj.getClass()) == cl)
 {
     break;
 }
 cl = repCl;
}

这个方法中会执行 entry = new ObjectStreamClass(cl);,而这个构造方法中,会涉及到writeObjectMethod和readObjectMethod两个变量的赋值。 下面代码的含义为找到被序列化或反序列类中的writeObject和readObject两个方法的引用赋值给writeObjectMethod和readObjectMethod两个变量。如果没有实现则为null,否则就有值。

writeObjectMethod = getPrivateMethod(cl, "writeObject",
 new Class<?>[] { ObjectOutputStream.class },
 Void.TYPE);
readObjectMethod = getPrivateMethod(cl, "readObject",
 new Class<?>[] { ObjectInputStream.class },
 Void.TYPE);

这样就能够实现执行自己所写的writeObject和readObject方法

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值