在上一篇文章中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 方法可以实现自定义序列化策略