对于Java自带的对象序列化,我并没有仔细研究过它的实现机制。对于Java默认的序列化机制,它的最大优点就是简单方便。你需要做的仅仅是对需要序列化的POJO增加一句implement Serializable,最多最多再增加一行serialVersionUID。其他的事情就不用我们操心了。那么Java默认的序列化机制是如何实现的呢?答案就是——反射。
Person person = new Person(25, "Tim");
FileOutputStream fos = new FileOutputStream("Person.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(person);
上面这段代码是典型的默认序列化使用方式。玄机就在ObjectOutputStream这个类中,它的writeObject()方法实现大致如下:
public final void writeObject(Object obj) throws IOException {
try {
writeObject0(obj, false);
} catch (IOException ex) {
if (depth == 0) {
writeFatalException(ex);
}
throw ex;
}
}
真正完成序列化任务的其实是writeObject0(),顺着writeObject0方法,继续看下去。这个方法其实是比较长的,writeObject0()方法使用了深度优先搜索实现了对象的序列化。比较重要的代码如下:
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
boolean oldMode = bout.setBlockDataMode(false);
depth++;
try {
// remaining cases
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());
}
}
} finally {
depth--;
bout.setBlockDataMode(oldMode);
}
}
writeObject0()方法调用了,会检查对象是不是实现了Serializable接口。如果实现了就会调用writeOrdinaryObject(),否者抛出异常。writeOrdinaryObject()最终会调用defaultWriteFields()方法,这个函数的主要代码如下:
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
throws IOException
{
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
desc.checkDefaultSerialize();
int primDataSize = desc.getPrimDataSize();
if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
}
desc.getPrimFieldValues(obj, primVals);
bout.write(primVals, 0, primDataSize, false);
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
desc.getObjFieldValues(obj, objVals);
for (int i = 0; i < objVals.length; i++) {
try {
writeObject0(objVals[i],
fields[numPrimFields + i].isUnshared());
} finally {
/* */
}
}
}
在这个函数中,将会依次写入基本类型的字段,然后写入对象类型的字段。其中写入非基本类型字段调用了writeObject0()方法。这个序列化过程就是这样以DFS的方式递归的完成的。
使用Java默认序列化方式时,需要注意的一点是对象的静态字段和transient字段不会被序列化。这个特性在源代码中也可以看出来,这部分功能主要由ObjectStreamClass类实现。在ObjectStreamClass类中,有这样的一个静态方法getDefaultSerialFields(),它的功能就是使用反射取出一个类的各个字段。这个函数的代码如下:
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
Field[] clFields = cl.getDeclaredFields();
ArrayList<ObjectStreamField> list = new ArrayList<>();
int mask = Modifier.STATIC | Modifier.TRANSIENT;
for (int i = 0; i < clFields.length; i++) {
if ((clFields[i].getModifiers() & mask) == 0) {
list.add(new ObjectStreamField(clFields[i], false, true));
}
}
int size = list.size();
return (size == 0) ? NO_FIELDS :
list.toArray(new ObjectStreamField[size]);
}
看到mask的时候,一切都水落石出了。返回的list是排除了mask所指代的字段。而这个ObjectStreamClass是作为参数传递给defaultWriteFields()方法的,所以static和transient字段根本不会再默认方式中被序列化。这篇blog到这里就完了,我没有分析序列化后的二进制格式,暂时还没有研究的兴趣。
发表回复