自己实现序列化和反序列化的过程
这个问题是我在进行单例模式学习的过程中遇到的,
如果实现了序列化接口, 还要做什么来防止反序列化破坏单例?
答:实现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方法