对于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(); ArrayListlist = 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到这里就完了,我没有分析序列化后的二进制格式,暂时还没有研究的兴趣。
发表评论