对于 Java 的序列化,以前我一直停留在最浅显的认知上,因为每次新建一个类自然而然就把序列化接口 Serializbale
给实现了。我不愿意做更深入的研究,因为会用就行了嘛。
但随着时间的推移,见到 Serializbale
的次数越来越多,我便对它产生了浓厚的兴趣。是时候花点时间研究研究了。
几个待思考的问题
- 为什么序列化一个对象时,仅需要实现 Serializable 接口就可以了。
- 通常我们序列化一个类时,为什么推荐的做法是要实现一个静态 final 成员变量 serialVersionUID。
- 序列化机制是怎么忽略 transient 关键字的, static变量也不会被序列化。
接下来我们就带着问题,在源码中找寻答案吧。
什么是序列化?
序列化:Java中的序列化机制能够将一个实例对象信息写入到一个字节流中(只序列化对象的属性值,而不会去序列化方法),序列化后的对象可用于网络传输,或者持久化到数据库、磁盘中。
反序列化:需要对象的时候,再通过字节流中的信息来重构一个相同的对象。
Java 中要使一个类可以序列化,实现 `java.io.Serializable 接口是最简单的。
public class User implements Serializable { private static final long serialVersionUID = 1L; } 复制代码
那么我们来看看 Serializable
接口的源码实现,可以看到 Serializable
接口中并没有方法或字段,这个接口仅仅用于标识可序列化的语义,也就是说它只是用来标识一个对象是否可被序列化。
package java.io; /** * @author unascribed * @see java.io.ObjectOutputStream * @see java.io.ObjectInputStream * @see java.io.ObjectOutput * @see java.io.ObjectInput * @see java.io.Externalizable * @since JDK1.1 */ public interface Serializable {} 复制代码
为什么要序列化?
- 把数据进行本地存储
- 我们知道数据只能以二进制的形式在网络中传输,JavaBean对象在网络中传输时所以也需要用到序列化技术
- IPC通信(因为进程内存的隔离,所以需要通过序列化传输数据)
接下来写一个例子测试一下:
创建一个 User 对象
@Datapublic class User { private String name; private String age; } 复制代码
再来创建一个测试类,通过 ObjectOutputStream
将“18 岁的王二”写入到文件当中,实际上就是一种序列化的过程;再通过 ObjectInputStream
将“18 岁的王二”从文件中读出来,实际上就是一种反序列化的过程。
public class Test { public static void main(String[] args) { // 初始化 User user = new User(); user.setName("王二"); user.setAge(18); System.out.println(user); // 把对象写到文件中 try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));){ oos.writeObject(user); } catch (IOException e) { e.printStackTrace(); } // 从文件中读出对象 try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));){ User user1 = (User) ois.readObject(); System.out.println(user1); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } } 复制代码
不过,由于 User 没有实现 Serializbale
接口,所以在运行测试类的时候会抛出异常,堆栈信息如下:
java.io.NotSerializableException: com.cmower.java_demo.xuliehua.Wanger at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at com.cmower.java_demo.xuliehua.Test.main(Test.java:21) 复制代码
接下来将 User 实现 Serializable
接口
@Data public class User implements Serializable { private static final long serialVersionUID = 1L; private String name; private String age; } 复制代码
@Slf4j public class serializeTest { public static void main(String[] args) throws Exception { User user = new User(); user.setName("fufu"); user.setAge("18"); serialize(user); log.info("Java序列化前的结果:{} ", user); User duser = deserialize(); log.info("Java反序列化的结果:{} ", duser); } /** * @author xzf * @description 序列化 * @date 2020/2/22 19:34 */ private static void serialize(User user) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:\\111.txt"))); oos.writeObject(user); oos.close(); } /** * @author xzf * @description 反序列化 * @date 2020/2/22 19:34 */ private static User deserialize() throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\111.txt"))); return (User) ois.readObject(); } } 复制代码
因为 User 实现了 Serializable
,此时就可以序列化和反序列化了,输出结果如下:
序列化前的结果: User(name=fufu, age=18) 反序列化后的结果: User(name=fufu, age=18) 复制代码
打开 writeObject
方法的源码看一下,发现方法中有这么一个逻辑,当要写入的对象是 String
、Array
、Enum
、Serializable
类型的对象则可以正常序列化,否则会抛出 NotSerializableException
异常。
这就能解释为什么Java序列化一定要实现 Serializable
接口了。
/** * Underlying writeObject/writeUnshared implementation. */ 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); } } 复制代码
那么可能会有人疑问,String
为啥就不用实现 Serializable
接口呢?其实 String
已经内部实现了 Serializable
,不用我们再显示实现。看看源码就懂了
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; ...... } 复制代码
也就是说,ObjectOutputStream
在序列化的时候,会判断被序列化的对象是哪一种类型,字符串?数组?枚举?还是 Serializable
,如果全都不是的话,抛出 NotSerializableException
。
既然已经实现了 Serializable
接口,为什么还要显示指定serialVersionUID
的值呢?
因为序列化对象时,如果不设置 serialVersionUID
,Java在序列化时会根据对象属性自动的生成一个 serialVersionUID
,再进行存储或用作网络传输。
在反序列化时,会根据对象属性自动再生成一个新的 serialVersionUID
,和序列化时生成的 serialVersionUID
进行比对,两个 serialVersionUID
相同则反序列化成功,否则就会抛异常。
而当手动设置 serialVersionUID
后,Java在序列化和反序列化对象时,生成的serialVersionUID
和我们设定的 serialVersionUID
相同,这样就保证了反序列化的成功。
transient
序列化对象时如果希望哪个属性不被序列化,则用 transient
关键字修饰即可
@Datapublic class User implements Serializable { private transient String name; private String age; } 复制代码
可以看到字段 name
的值没有被保存到磁盘中,一旦变量被 transient
修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
Java序列化前的结果: User(name=fufu, age=18) Java反序列化的结果:User(name=null, age=18) 复制代码
一个静态变量不管是否被 transient
修饰,均不能被序列化。因为 static
修饰的属性是属于类,而非对象。
为什么 transient
不被序列化呢?transient
的中文字义为“临时的”(论英语的重要性),它可以阻止字段被序列化到文件中,在被反序列化后,transient
字段的值被设为初始值,比如 int
型的初始值为 0,对象型的初始值为 null
。
如果想要深究源码的话,你可以在 ObjectStreamClass
中发现下面这样的代码:
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) { Field[] clFields = cl.getDeclaredFields(); ArrayList<ObjectStreamField> list = new ArrayList<>(); int mask = Modifier.STATIC | Modifier.TRANSIENT; int size = list.size(); return (size == 0) ? NO_FIELDS : list.toArray(new ObjectStreamField[size]); } 复制代码
复制代码看到 Modifier.STATIC
| Modifier.TRANSIENT
,是不是感觉更好了呢?
序列化过程
以 ObjectOutputStream 为例:
进入writeObject()方法。
public final void writeObject(Object obj) throws IOException { if (enableOverride) {//初始化时设置了false,不会走这里 writeObjectOverride(obj); return; } try { writeObject0(obj, false);//直接调用了这个方法 } catch (IOException ex) { if (depth == 0) { // BEGIN Android-changed: Ignore secondary exceptions during writeObject(). // writeFatalException(ex); try { writeFatalException(ex); } catch (IOException ex2) { // If writing the exception to the output stream causes another exception there // is no need to propagate the second exception or generate a third exception, // both of which might obscure details of the root cause. } // END Android-changed: Ignore secondary exceptions during writeObject(). } throw ex; } } 复制代码
进入writeObject0()方法。
/** * Underlying writeObject/writeUnshared implementation. */ private void writeObject0(Object obj, boolean unshared) throws IOException { boolean oldMode = bout.setBlockDataMode(false); depth++; try { //... 代码省略 // check for replacement object Object orig = obj; Class<?> cl = obj.getClass(); ObjectStreamClass desc;//重点关注这个类 //...代码省略 Class repCl; desc = ObjectStreamClass.lookup(cl, true);//对desc对象进行了初始化 if (desc.hasWriteReplaceMethod() && (obj = desc.invokeWriteReplace(obj)) != null && (repCl = obj.getClass()) != cl) { cl = repCl; desc = ObjectStreamClass.lookup(cl, true); } // END Android-changed: Make only one call to writeReplace. if (enableReplace) { Object rep = replaceObject(obj); if (rep != obj && rep != null) { cl = rep.getClass(); desc = ObjectStreamClass.lookup(cl, true); } obj = rep; } //...代码省略 //可以看出下面这些类型都是可以进行写入操作的 if (obj instanceof Class) { writeClass((Class) obj, unshared); } else if (obj instanceof ObjectStreamClass) { writeClassDesc((ObjectStreamClass) obj, unshared); } else 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) { //实现Serializable会执行下面这个方法 //第一个问题序列化为什么要实现Serializable?这就有了答案,Serializable只是一个标记接口,本身没有任何意义。 writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException( cl.getName() + "\n" + debugInfoStack.toString()); } else { //所以没有实现Serializable接口是会报错的 throw new NotSerializableException(cl.getName()); } } } finally { depth--; bout.setBlockDataMode(oldMode); } } 复制代码
在writeObject0()方法中做了两件重要的事
- 通过ObjectStreamClass.lookup(cl, true)方法,初始化了ObjectStreamClass对象
- 根据不同写入类型,进行不同的操作
首先,我们先进入ObjectStreamClass.lookup(cl, true)方法。
static ObjectStreamClass lookup(Class<?> cl, boolean all) { if (!(all || Serializable.class.isAssignableFrom(cl))) { return null; } processQueue(Caches.localDescsQueue, Caches.localDescs); WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue); //下面先是从缓存中获取对象,首次进入都是没有 Reference<?> ref = Caches.localDescs.get(key); Object entry = null; if (ref != null) { entry = ref.get(); } EntryFuture future = null; if (entry == null) { EntryFuture newEntry = new EntryFuture(); Reference<?> newRef = new SoftReference<>(newEntry); do { if (ref != null) { Caches.localDescs.remove(key, ref); } ref = Caches.localDescs.putIfAbsent(key, newRef); if (ref != null) { entry = ref.get(); } } while (ref != null && entry == null); if (entry == null) { future = newEntry; } } //如果缓存中存在,就直接返回 if (entry instanceof ObjectStreamClass) { // check common case first return (ObjectStreamClass) entry; } if (entry instanceof EntryFuture) { future = (EntryFuture) entry; if (future.getOwner() == Thread.currentThread()) { /* * Handle nested call situation described by 4803747: waiting * for future value to be set by a lookup() call further up the * stack will result in deadlock, so calculate and set the * future value here instead. */ entry = null; } else { entry = future.get(); } } if (entry == null) { try { //缓存获取失败,直接创建对象 entry = new ObjectStreamClass(cl); } catch (Throwable th) { entry = th; } if (future.set(entry)) { Caches.localDescs.put(key, new SoftReference<Object>(entry));//存入缓存 } else { // nested lookup call already set future entry = future.get(); } } if (entry instanceof ObjectStreamClass) { return (ObjectStreamClass) entry; } else if (entry instanceof RuntimeException) { throw (RuntimeException) entry; } else if (entry instanceof Error) { throw (Error) entry; } else { throw new InternalError("unexpected entry: " + entry); } } 复制代码
再进入ObjectStreamClass构造方法。
private ObjectStreamClass(final Class<?> cl) { this.cl = cl; //获取了类的一些基本信息 name = cl.getName(); isProxy = Proxy.isProxyClass(cl); isEnum = Enum.class.isAssignableFrom(cl); serializable = Serializable.class.isAssignableFrom(cl); externalizable = Externalizable.class.isAssignableFrom(cl); Class<?> superCl = cl.getSuperclass();//获取父类类型 superDesc = (superCl != null) ? lookup(superCl, false) : null;//父类存在,接着调用lookup方法,一直向上递归,直到父类为null。 localDesc = this; if (serializable) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { if (isEnum) { suid = Long.valueOf(0); fields = NO_FIELDS; return null; } if (cl.isArray()) { fields = NO_FIELDS; return null; } suid = getDeclaredSUID(cl);//这里获取了serialVersionUID,如果序列化类没有声明将为null。 try { fields = getSerialFields(cl);//最重要的一点,获取序列化的属性 computeFieldOffsets(); } catch (InvalidClassException e) { serializeEx = deserializeEx = new ExceptionInfo(e.classname, e.getMessage()); fields = NO_FIELDS; } if (externalizable) { cons = getExternalizableConstructor(cl); } else { cons = getSerializableConstructor(cl); //如果序列化类重写了writeObject()和readObject()方法。 writeObjectMethod = getPrivateMethod(cl, "writeObject", new Class<?>[] { ObjectOutputStream.class }, Void.TYPE); readObjectMethod = getPrivateMethod(cl, "readObject", new Class<?>[] { ObjectInputStream.class }, Void.TYPE); readObjectNoDataMethod = getPrivateMethod( cl, "readObjectNoData", null, Void.TYPE); hasWriteObjectData = (writeObjectMethod != null); } writeReplaceMethod = getInheritableMethod( cl, "writeReplace", null, Object.class); readResolveMethod = getInheritableMethod( cl, "readResolve", null, Object.class); return null; } }); } else { suid = Long.valueOf(0); fields = NO_FIELDS; } try { fieldRefl = getReflector(fields, this); } catch (InvalidClassException ex) { // field mismatches impossible when matching local fields vs. self throw new InternalError(ex); } if (deserializeEx == null) { if (isEnum) { deserializeEx = new ExceptionInfo(name, "enum type"); } else if (cons == null) { deserializeEx = new ExceptionInfo(name, "no valid constructor"); } } for (int i = 0; i < fields.length; i++) { if (fields[i].getField() == null) { defaultSerializeEx = new ExceptionInfo( name, "unmatched serializable field(s) declared"); } } initialized = true; } 复制代码
接着进入getSerialFields()方法。
private static ObjectStreamField[] getSerialFields(Class<?> cl) throws InvalidClassException { ObjectStreamField[] fields; if (Serializable.class.isAssignableFrom(cl) && !Externalizable.class.isAssignableFrom(cl) && !Proxy.isProxyClass(cl) && !cl.isInterface()) { if ((fields = getDeclaredSerialFields(cl)) == null) { fields = getDefaultSerialFields(cl);//又调用了这里 } Arrays.sort(fields);//进行升序排列 } else { fields = NO_FIELDS; } return fields; } 复制代码
接着进入getDefaultSerialFields()方法。
private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) { Field[] clFields = cl.getDeclaredFields(); ArrayList<ObjectStreamField> list = new ArrayList<>(); int mask = Modifier.STATIC | Modifier.TRANSIENT; //看到了STATIC和TRANSIENT关键词 for (int i = 0; i < clFields.length; i++) { if ((clFields[i].getModifiers() & mask) == 0) { //通过这一行代码,过滤掉了被STATIC和TRANSIENT修饰的序列化属性,这是第三个问题的答案! list.add(new ObjectStreamField(clFields[i], false, true)); } } int size = list.size(); return (size == 0) ? NO_FIELDS : list.toArray(new ObjectStreamField[size]); } 复制代码
ObjectStreamClass相关的主要工作基本上也就结束了,在初始化中主要完成了对序列化对象的,基本信息、serialVersionUID、属性的收集;当然也包括往上所有的父类,是一个递归的过程。
我们回到writeObject0()方法中对Serializable数据的写入操作,writeOrdinaryObject(obj, desc, unshared)方法。
private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException { if (extendedDebugInfo) { debugInfoStack.push( (depth == 1 ? "root " : "") + "object (class "" + obj.getClass().getName() + "", " + obj.toString() + ")"); } try { desc.checkSerialize(); bout.writeByte(TC_OBJECT);//写入Object标识 writeClassDesc(desc, false);//写了一些类信息 handles.assign(unshared ? null : obj); if (desc.isExternalizable() && !desc.isProxy()) { writeExternalData((Externalizable) obj); } else { writeSerialData(obj, desc);//继续调用这个方法 } } finally { if (extendedDebugInfo) { debugInfoStack.pop(); } } } 复制代码
writeOrdinaryObject()方法重点调用了两个方法,writeClassDesc()和writeSerialData();我们先看writeClassDesc()。
private void writeClassDesc(ObjectStreamClass desc, boolean unshared) throws IOException { int handle; if (desc == null) { //对象为null,写入null标识 writeNull(); } else if (!unshared && (handle = handles.lookup(desc)) != -1) { writeHandle(handle); } else if (desc.isProxy()) { writeProxyDesc(desc, unshared); } else { writeNonProxyDesc(desc, unshared);//一般都会调用这个方法 } } 复制代码
进入writeNonProxyDesc()方法。
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) throws IOException { bout.writeByte(TC_CLASSDESC);//写入类元信息标记位 handles.assign(unshared ? null : desc); if (protocol == PROTOCOL_VERSION_1) { // do not invoke class descriptor write hook with old protocol desc.writeNonProxy(this); } else { writeClassDescriptor(desc);//重点在这里,写入了类的描述符信息 } Class<?> cl = desc.forClass(); bout.setBlockDataMode(true); if (cl != null && isCustomSubclass()) { ReflectUtil.checkPackageAccess(cl); } annotateClass(cl); bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); writeClassDesc(desc.getSuperDesc(), false);//又调用writeClassDesc(),传入父类初始化了ObjectStreamClass对象,进入递归模式,写入类的描述符信息。 } 复制代码
进入writeClassDescriptor()方法。
protected void writeClassDescriptor(ObjectStreamClass desc) { desc.writeNonProxy(this); } 复制代码
又再调用了ObjectStreamClass对象本身的writeNonProxy()方法。
void writeNonProxy(ObjectOutputStream out) throws IOException { out.writeUTF(name);//写入类名 out.writeLong(getSerialVersionUID());//再次出现serialVersionUID,这里被写入 //判断类类型的标识 byte flags = 0; if (externalizable) { flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; int protocol = out.getProtocolVersion(); if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) { flags |= ObjectStreamConstants.SC_BLOCK_DATA; } } else if (serializable) { flags |= ObjectStreamConstants.SC_SERIALIZABLE; } if (hasWriteObjectData) { flags |= ObjectStreamConstants.SC_WRITE_METHOD; } if (isEnum) { flags |= ObjectStreamConstants.SC_ENUM; } //写入类类型标识 out.writeByte(flags); //写入序列化属性的信息 out.writeShort(fields.length); for (int i = 0; i < fields.length; i++) { ObjectStreamField f = fields[i]; out.writeByte(f.getTypeCode()); out.writeUTF(f.getName()); if (!f.isPrimitive()) { out.writeTypeString(f.getTypeString()); } } } 复制代码
writeNonProxy()方法中,写入了很多信息,我们还需要看一下getSerialVersionUID()方法。
public long getSerialVersionUID() { // REMIND: synchronize instead of relying on volatile? if (suid == null) {//如果ObjectStreamClass初始化时,序列化类没有定义SerialVersionUID,那么suid就是null值。 suid = AccessController.doPrivileged( new PrivilegedAction<Long>() { public Long run() { //获取默认的值,所以这就是第2个问题的一部分答案,为什么要在序列类中定义 //SerialVersionUID,因为如果不定义就会默认生产,而这个默认的值很可能就因为类内容的改变而改变。会产 //生什么具体的影响,后面继续分析! return computeDefaultSUID(cl); } } ); } return suid.longValue(); } 复制代码
分析完writeClassDesc()的流程,我们接着分析writeSerialData()方法,写入真正的数据。
private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();//获取数据布局的ClassDataSlot实例数组 for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; //如果序列化对象实现了自己的writeObject()方法,进入if。 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);//一般都是调用这里 } } } 复制代码
我们先看下如何获取数据布局的ClassDataSlot实例数组的。
ClassDataSlot[] getClassDataLayout() throws InvalidClassException { // REMIND: synchronize instead of relying on volatile? if (dataLayout == null) { dataLayout = getClassDataLayout0(); } return dataLayout; } 复制代码
又调用了getClassDataLayout0()方法。
private ClassDataSlot[] getClassDataLayout0() throws InvalidClassException { ArrayList<ClassDataSlot> slots = new ArrayList<>(); Class<?> start = cl, end = cl; // locate closest non-serializable superclass while (end != null && Serializable.class.isAssignableFrom(end)) { end = end.getSuperclass(); } HashSet<String> oscNames = new HashSet<>(3); for (ObjectStreamClass d = this; d != null; d = d.superDesc) { if (oscNames.contains(d.name)) { throw new InvalidClassException("Circular reference."); } else { oscNames.add(d.name); } // search up inheritance hierarchy for class with matching name String searchName = (d.cl != null) ? d.cl.getName() : d.name; Class<?> match = null; for (Class<?> c = start; c != end; c = c.getSuperclass()) { if (searchName.equals(c.getName())) { match = c; break; } } //通过遍历,把所有的ObjectStreamClass对象包装成了ClassDataSlot对象 // add "no data" slot for each unmatched class below match if (match != null) { for (Class<?> c = start; c != match; c = c.getSuperclass()) { slots.add(new ClassDataSlot( ObjectStreamClass.lookup(c, true), false)); } start = match.getSuperclass(); } // record descriptor/class pairing slots.add(new ClassDataSlot(d.getVariantFor(match), true)); } // add "no data" slot for any leftover unmatched classes for (Class<?> c = start; c != end; c = c.getSuperclass()) { slots.add(new ClassDataSlot( ObjectStreamClass.lookup(c, true), false)); } // order slots from superclass -> subclass Collections.reverse(slots);//调用了reverse反转,所以最顶端的父类ClassDataSlot对象在前。 return slots.toArray(new ClassDataSlot[slots.size()]); } 复制代码
把所有的ObjectStremClass对象再封装成了ClassDataSlot对象,并且进行了倒序排列。回到writeSerialData()方法中,继续进入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]; } //获取基本数据类型的实例数据,并存入primVals数组 desc.getPrimFieldValues(obj, primVals); //写入primVals数组中的数据,所以基本数据类型在这里就直接写入了。 bout.write(primVals, 0, primDataSize, false); ObjectStreamField[] fields = desc.getFields(false); Object[] objVals = new Object[desc.getNumObjFields()]; int numPrimFields = fields.length - objVals.length; //获取未写入的对象数据,存入objVals数组 desc.getObjFieldValues(obj, objVals); for (int i = 0; i < objVals.length; i++) { if (extendedDebugInfo) { debugInfoStack.push( "field (class "" + desc.getName() + "", name: "" + fields[numPrimFields + i].getName() + "", type: "" + fields[numPrimFields + i].getType() + "")"); } try { //再调用writeObject0方法,重新进行类型判断,进行不同的写入处理;也是一种递归处理,直到所有数据写入完成 writeObject0(objVals[i], fields[numPrimFields + i].isUnshared()); } finally { if (extendedDebugInfo) { debugInfoStack.pop(); } } } } 复制代码
到这里,序列化的过程就结束了。
反序列化过程
以 ObjectInputStream 为例:
下面我们进入关键的readObject()方法。
public final Object readObject() throws IOException, ClassNotFoundException { if (enableOverride) {//初始化设置成了false,不会走。 return readObjectOverride(); } // if nested read, passHandle contains handle of enclosing object int outerHandle = passHandle; try { Object obj = readObject0(false);//接着会调用这里 handles.markDependency(outerHandle, passHandle); ClassNotFoundException ex = handles.lookupException(passHandle); if (ex != null) { throw ex; } if (depth == 0) { vlist.doCallbacks(); } return obj; } finally { passHandle = outerHandle; if (closed && depth == 0) { clear(); } } } 复制代码
进入readObject0()方法。
private Object readObject0(boolean unshared) throws IOException { boolean oldMode = bin.getBlockDataMode(); if (oldMode) { int remain = bin.currentBlockRemaining(); if (remain > 0) { throw new OptionalDataException(remain); } else if (defaultDataEnd) { /* * Fix for 4360508: stream is currently at the end of a field * value block written via default serialization; since there * is no terminating TC_ENDBLOCKDATA tag, simulate * end-of-custom-data behavior explicitly. */ throw new OptionalDataException(true); } bin.setBlockDataMode(false); } byte tc;//读取数据类型标识 while ((tc = bin.peekByte()) == TC_RESET) { bin.readByte(); handleReset(); } depth++; // Android-removed: ObjectInputFilter logic, to be reconsidered. http://b/110252929 // totalObjectRefs++; try { switch (tc) { case TC_NULL: return readNull(); case TC_REFERENCE: return readHandle(unshared); case TC_CLASS: return readClass(unshared); case TC_CLASSDESC: case TC_PROXYCLASSDESC: return readClassDesc(unshared); case TC_STRING: case TC_LONGSTRING: return checkResolve(readString(unshared)); case TC_ARRAY: return checkResolve(readArray(unshared)); case TC_ENUM: return checkResolve(readEnum(unshared)); case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared));//进入这里 case TC_EXCEPTION: IOException ex = readFatalException(); throw new WriteAbortedException("writing aborted", ex); case TC_BLOCKDATA: case TC_BLOCKDATALONG: if (oldMode) { bin.setBlockDataMode(true); bin.peek(); // force header read throw new OptionalDataException( bin.currentBlockRemaining()); } else { throw new StreamCorruptedException( "unexpected block data"); } case TC_ENDBLOCKDATA: if (oldMode) { throw new OptionalDataException(true); } else { throw new StreamCorruptedException( "unexpected end of block data"); } default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } } finally { depth--; bin.setBlockDataMode(oldMode); } } 复制代码
我们进入readOrdinaryObject()方法。
private Object readOrdinaryObject(boolean unshared) throws IOException { if (bin.readByte() != TC_OBJECT) { throw new InternalError(); } ObjectStreamClass desc = readClassDesc(false);//读取类的描述信息 desc.checkDeserialize(); Class<?> cl = desc.forClass(); if (cl == String.class || cl == Class.class || cl == ObjectStreamClass.class) { throw new InvalidClassException("invalid class descriptor"); } Object obj; try { obj = desc.isInstantiable() ? desc.newInstance() : null; } catch (Exception ex) { throw (IOException) new InvalidClassException( desc.forClass().getName(), "unable to create instance").initCause(ex); } passHandle = handles.assign(unshared ? unsharedMarker : obj); ClassNotFoundException resolveEx = desc.getResolveException(); if (resolveEx != null) { handles.markException(passHandle, resolveEx); } if (desc.isExternalizable()) { readExternalData((Externalizable) obj, desc); } else { readSerialData(obj, desc);//读取序列化数据 } handles.finish(passHandle); if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { Object rep = desc.invokeReadResolve(obj); if (unshared && rep.getClass().isArray()) { rep = cloneArray(rep); } if (rep != obj) { // Android-removed: ObjectInputFilter logic, to be reconsidered. http://b/110252929 /* // Filter the replacement object if (rep != null) { if (rep.getClass().isArray()) { filterCheck(rep.getClass(), Array.getLength(rep)); } else { filterCheck(rep.getClass(), -1); } } */ handles.setObject(passHandle, obj = rep); } } return obj; } 复制代码
我们先看下readClassDesc()方法。
private ObjectStreamClass readClassDesc(boolean unshared) throws IOException { byte tc = bin.peekByte();//获取类元信息标记位 ObjectStreamClass descriptor; switch (tc) { case TC_NULL: descriptor = (ObjectStreamClass) readNull(); break; case TC_REFERENCE: descriptor = (ObjectStreamClass) readHandle(unshared); break; case TC_PROXYCLASSDESC: descriptor = readProxyDesc(unshared); break; case TC_CLASSDESC: descriptor = readNonProxyDesc(unshared);//会调用这里 break; default: throw new StreamCorruptedException( String.format("invalid type code: %02X", tc)); } // Android-removed: ObjectInputFilter logic, to be reconsidered. http://b/110252929 // if (descriptor != null) { // validateDescriptor(descriptor); // } return descriptor; } 复制代码
进入readNonProxyDesc()方法。
private ObjectStreamClass readNonProxyDesc(boolean unshared) throws IOException { if (bin.readByte() != TC_CLASSDESC) { throw new InternalError(); } ObjectStreamClass desc = new ObjectStreamClass(); int descHandle = handles.assign(unshared ? unsharedMarker : desc); passHandle = NULL_HANDLE; ObjectStreamClass readDesc = null; try { readDesc = readClassDescriptor();//读取存储的序列化类的描述信息 } catch (ClassNotFoundException ex) { throw (IOException) new InvalidClassException( "failed to read class descriptor").initCause(ex); } Class<?> cl = null; ClassNotFoundException resolveEx = null; bin.setBlockDataMode(true); final boolean checksRequired = isCustomSubclass(); try { if ((cl = resolveClass(readDesc)) == null) { resolveEx = new ClassNotFoundException("null class"); } else if (checksRequired) { ReflectUtil.checkPackageAccess(cl); } } catch (ClassNotFoundException ex) { resolveEx = ex; } skipCustomData(); desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));//初始化类描述符 // Android-removed: ObjectInputFilter unsupported - removed filterCheck() call. // // Call filterCheck on the definition // filterCheck(desc.forClass(), -1); handles.finish(descHandle); passHandle = descHandle; return desc; } 复制代码
readNonProxyDesc()方法,进行了两个非常关键的处理
- 通过调用readClassDescriptor()方法读取了存储的序列化类的描述信息,并初始化了ObjectStreamClass对象,赋值给了readDesc属性;
- 通过调用新创建的ObjectStreamClass对象desc的initNonProxy()方法,进行了初始化;
这里需要区分一下,readDesc局部属性对象是通过readClassDescriptor()方法创建的,而desc属性对象则是新创建的对象再通过initNonProxy()方法进行初始化,我们先看一下readClassDescriptor()方法。
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass desc = new ObjectStreamClass(); desc.readNonProxy(this); return desc; } 复制代码
创建了一个ObjectStreamClass对象,直接再调用readNonProxy()方法。
void readNonProxy(ObjectInputStream in) throws IOException, ClassNotFoundException { name = in.readUTF();//类名 suid = Long.valueOf(in.readLong());//熟悉吧,序列化写入的serialVersionUID回来了 isProxy = false; byte flags = in.readByte(); hasWriteObjectData = ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0); hasBlockExternalData = ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0); externalizable = ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0); boolean sflag = ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0); if (externalizable && sflag) { throw new InvalidClassException( name, "serializable and externalizable flags conflict"); } serializable = externalizable || sflag; isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0); if (isEnum && suid.longValue() != 0L) { throw new InvalidClassException(name, "enum descriptor has non-zero serialVersionUID: " + suid); } int numFields = in.readShort(); if (isEnum && numFields != 0) { throw new InvalidClassException(name, "enum descriptor has non-zero field count: " + numFields); } fields = (numFields > 0) ? new ObjectStreamField[numFields] : NO_FIELDS; for (int i = 0; i < numFields; i++) { //循环读取序列化属性信息 char tcode = (char) in.readByte(); String fname = in.readUTF(); String signature = ((tcode == 'L') || (tcode == '[')) ? in.readTypeString() : new String(new char[] { tcode }); try { fields[i] = new ObjectStreamField(fname, signature, false); } catch (RuntimeException e) { throw (IOException) new InvalidClassException(name, "invalid descriptor for field " + fname).initCause(e); } } computeFieldOffsets(); } 复制代码
分析完,我们再回过头去看新创建的ObjectStreamClass对象对initNonProxy()方法的调用。
void initNonProxy(ObjectStreamClass model, Class<?> cl, ClassNotFoundException resolveEx, ObjectStreamClass superDesc) throws InvalidClassException { //需要重点标注一下,参数model就是readDesc,cl是传入的序列化类 long suid = Long.valueOf(model.getSerialVersionUID());//获取了序列化类的serialVersionUID() ObjectStreamClass osc = null; if (cl != null) { osc = lookup(cl, true); if (osc.isProxy) { throw new InvalidClassException( "cannot bind non-proxy descriptor to a proxy class"); } if (model.isEnum != osc.isEnum) { throw new InvalidClassException(model.isEnum ? "cannot bind enum descriptor to a non-enum class" : "cannot bind non-enum descriptor to an enum class"); } if (model.serializable == osc.serializable && !cl.isArray() && suid != osc.getSerialVersionUID()) {//重点、重点、重点 ...... //这就是第二个问题答案,serialVersionUID的作用,通过序列化缓存对象的 //serialVersionUID和传入的序列化类的serialVersionUID对比来判断版本是否一致,这 //也是为什么建议自己定义serialVersionUID的原因,避免类的改动导致serialVersionUID的变化而版本冲突报错。 throw new InvalidClassException(osc.name, "local class incompatible: " + "stream classdesc serialVersionUID = " + suid + ", local class serialVersionUID = " + osc.getSerialVersionUID()); } if (!classNamesEqual(model.name, osc.name)) { throw new InvalidClassException(osc.name, "local class name incompatible with stream class " + "name "" + model.name + """); } if (!model.isEnum) { if ((model.serializable == osc.serializable) && (model.externalizable != osc.externalizable)) { throw new InvalidClassException(osc.name, "Serializable incompatible with Externalizable"); } if ((model.serializable != osc.serializable) || (model.externalizable != osc.externalizable) || !(model.serializable || model.externalizable)) { deserializeEx = new ExceptionInfo( osc.name, "class invalid for deserialization"); } } } //初始化参数赋值 this.cl = cl; this.resolveEx = resolveEx; this.superDesc = superDesc; name = model.name; this.suid = suid; isProxy = false; isEnum = model.isEnum; serializable = model.serializable; externalizable = model.externalizable; hasBlockExternalData = model.hasBlockExternalData; hasWriteObjectData = model.hasWriteObjectData; fields = model.fields; primDataSize = model.primDataSize; numObjFields = model.numObjFields; if (osc != null) { localDesc = osc; writeObjectMethod = localDesc.writeObjectMethod; readObjectMethod = localDesc.readObjectMethod; readObjectNoDataMethod = localDesc.readObjectNoDataMethod; writeReplaceMethod = localDesc.writeReplaceMethod; readResolveMethod = localDesc.readResolveMethod; if (deserializeEx == null) { deserializeEx = localDesc.deserializeEx; } cons = localDesc.cons; } fieldRefl = getReflector(fields, localDesc); // reassign to matched fields so as to reflect local unshared settings fields = fieldRefl.getFields(); initialized = true; } 复制代码
我们再回到readOrdinaryObject()方法去看readSerialData()方法,来获取属性数据。
private void readSerialData(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 (slots[i].hasData) { if (obj == null || handles.lookupException(passHandle) != null) { defaultReadFields(null, slotDesc); // skip field values } else if (slotDesc.hasReadObjectMethod()) {//如果重写了readObject()方法 SerialCallbackContext oldContext = curContext; if (oldContext != null) oldContext.check(); try { curContext = new SerialCallbackContext(obj, slotDesc); bin.setBlockDataMode(true); slotDesc.invokeReadObject(obj, this); } catch (ClassNotFoundException ex) { /* * In most cases, the handle table has already * propagated a CNFException to passHandle at this * point; this mark call is included to address cases * where the custom readObject method has cons'ed and * thrown a new CNFException of its own. */ handles.markException(passHandle, ex); } finally { curContext.setUsed(); if (oldContext!= null) oldContext.check(); curContext = oldContext; // END Android-changed: ThreadDeath cannot cause corruption on Android. } /* * defaultDataEnd may have been set indirectly by custom * readObject() method when calling defaultReadObject() or * readFields(); clear it to restore normal read behavior. */ defaultDataEnd = false; } else { defaultReadFields(obj, slotDesc);//会执行到这里 } if (slotDesc.hasWriteObjectData()) { skipCustomData(); } else { bin.setBlockDataMode(false); } } else { if (obj != null && slotDesc.hasReadObjectNoDataMethod() && handles.lookupException(passHandle) == null) { slotDesc.invokeReadObjectNoData(obj); } } } } 复制代码
进入到defaultReadFields()方法中。
private void defaultReadFields(Object obj, ObjectStreamClass desc) throws IOException { Class<?> cl = desc.forClass(); if (cl != null && obj != null && !cl.isInstance(obj)) { throw new ClassCastException(); } int primDataSize = desc.getPrimDataSize(); if (primVals == null || primVals.length < primDataSize) { primVals = new byte[primDataSize]; } bin.readFully(primVals, 0, primDataSize, false);//从流中读取基本数据类型的值 if (obj != null) { desc.setPrimFieldValues(obj, primVals);//把基本数据类型的值设置到序列化类中,obj就是传入的序列化类 } int objHandle = passHandle; ObjectStreamField[] fields = desc.getFields(false); Object[] objVals = new Object[desc.getNumObjFields()]; int numPrimFields = fields.length - objVals.length; //遍历读取对象类型的序列化值 for (int i = 0; i < objVals.length; i++) { ObjectStreamField f = fields[numPrimFields + i]; objVals[i] = readObject0(f.isUnshared());//递归调用,回到readObject0()再根据不同的数据类型进行处理。 if (f.getField() != null) { handles.markDependency(objHandle, passHandle); } } if (obj != null) { desc.setObjFieldValues(obj, objVals); } passHandle = objHandle; } 复制代码
到这里,反序列化也完成了。
序列化和反序列化流程图
总结
实现Serializable序列化接口就是起一个标识作用,无任何实质意义。
serialVersionUID是用来判断序列化类的版本,重写serialVersionUID是为了防止因为序列化类的变动,导致默认生成的serialVersionUID不一致而冲突报错。
transient和static修饰的属性会在ObjectStreamClass类初始化时,getDefaultSerialFields() 方法中直接过滤掉了。