简单的写一个hello world程序,用不到序列化和反序列化。写一个排序算法也用不到序列化和反序列化。可是当你想要将一个对象进行持久化写入文件,或者你想将一个对象从一个网络地址经过网络协议发送到另外一个网络地址时,这时候就须要考虑序列化和反序列化了。另外若是你想对一个对象实例进行深度拷贝,也能够经过序列化和反序列化的方式进行。java
Serialization-序列化:能够看作是将一个对象转化为二进制流的过程算法
Deserialization-反序列化:能够看作是将对象的二进制流从新读取转换成对象的过程网络
只有实现了 Serializable 或 Externalizable 接口的类的对象才能被序列化,不然抛出异常。dom
Serializable接口
Serializable接口是Java提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化操做。使用Serializable来实现的对象的序列化至关简单,只须要在类的生命中指定一个相似相面的标识便可自动实现默认的序列化过程。这种方式是Java提供的一种序列化方式,过程很是简单,甚至有些开发人员都不须要声明serialVersionUID也能够完成这个过程,但serialVersionUID到底需不须要指定呢?须要!Java API既然提供了这个serialVersionUID,那么它一定是有用的。这个serialVersionUID是用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才可以正常地被反序列化。serialVersionUID的详细工做过程是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的二进制文件中,当反序列化的时候系统会检测文件中的serialVersionUID是否和当前类的serialVersionUID一致,若是一致就说明序列化的类的版本和当前类的版本是相同的,这个时候能够成功反序列化;不然说明当前类和反序列化的类相比发生了某些变化,好比成员变量的数量、类型发生了变化,这个时候是没法正常反序列化的。通常来讲,咱们应该手动指定serialVersionUID的值,好比1L,也可让IDE根据当前类的结构自动去生成它的hash值,这样序列化和反序列化时二者的serialVersionUID是相同的,所以能够正常进行反序列化操做。若是不手动指定serialVersionUID的值反序列化时当前类有些改变,好比增长或者删除了某些成员变量,那么系统就会从新计算当前类的hash值并把它赋值给serialVersionUID,这个时候当前类的serialVersionUID就和反序列化数据中的serialVersionUID不一致,就会形成反序列化失败的结果。因此,手动指定serialVersionUID能够在很大程度上避免反序列化过程的失败。好比当版本升级后,咱们可能删除了某个成员变量也可能增长了一些新的成员变量,这个时候咱们的反序列化过程依然可以成功,程序仍然可以最大限度地回复数据;相反,若是不指定serialVersionUID的话,程序会发生Crash。固然,咱们还须要考虑一种状况, 若是类结构发生了很是规改变,好比修改了类名,修改了成员变量的类型,这个时候尽管serialVersionUID验证经过了,可是反序列化过程仍然会失败,由于类的结构有了毁灭性的改变,根本没法从老版本的数据中还原出一个新的类结构的对象。
对于实现了这两个接口,具体序列化和反序列化的过程又分如下3种类状况:ide
ObjectOutputStream采用默认的序列化方式,对对象的非transient的实例变量进行序列化。
ObjcetInputStream采用默认的反序列化方式,对对象的非transient的实例变量进行反序列化。函数
ObjectOutputStream调用对象的writeObject(ObjectOutputStream out)的方法进行序列化。
ObjectInputStream会调用对象的readObject(ObjectInputStream in)的方法进行反序列化。ui
ObjectOutputStream调用对象的writeExternal(ObjectOutput out))的方法进行序列化。
ObjectInputStream会调用对象的readExternal(ObjectInput in)的方法进行反序列化。this
为了进一步说明,咱们直接看jdk底层ArrayList的序列化和反序列化:spa
// 实现了Serializable接口,能够被序列化 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8683452581122892189L; /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. */ // 实际元素被transient修饰,默认不会进行序列化 private transient Object[] elementData; ..... /** * Save the state of the <tt>ArrayList</tt> instance to a stream (that * is, serialize it). * * @serialData The length of the array backing the <tt>ArrayList</tt> * instance is emitted (int), followed by all of its elements * (each an <tt>Object</tt>) in the proper order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out array length s.writeInt(elementData.length); // Write out all elements in the proper order. for (int i=0; i<size; i++) s.writeObject(elementData[i]); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } } /** * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is, * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); // Read in array length and allocate array int arrayLength = s.readInt(); Object[] a = elementData = new Object[arrayLength]; // Read in all elements in the proper order. for (int i=0; i<size; i++) a[i] = s.readObject(); } }
能够看到,初看之下ArrayList的实际存储元素不能被序列化。但实际上根据咱们上面的第二条原则,知道由于其重写了writeObject和readObject方法,而在方法的内部实现了对具体存储对象的序列化与反序列化。那么这两个方法到底是在何时执行的呢?咱们须要转到ObjectOutputStream这个对象上来:debug
/** * Serialization's descriptor for classes. It contains the name and * serialVersionUID of the class. The ObjectStreamClass for a specific class * loaded in this Java VM can be found/created using the lookup method. 16 */ // 在序列化对象以前会封装一个ObjectStreamClass对象 public class ObjectStreamClass implements Serializable { /** class-defined writeObject method, or null if none */ private Method writeObjectMethod; /** * Creates local class descriptor representing given class. */ private ObjectStreamClass(final Class cl) { 36 ...... if (serializable) { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { if (isEnum) { suid = Long.valueOf(0); fields = NO_FIELDS; return null; } if (cl.isArray()) { fields = NO_FIELDS; return null; } suid = getDeclaredSUID(cl); try { fields = getSerialFields(cl); computeFieldOffsets(); } catch (InvalidClassException e) { serializeEx = deserializeEx = e; fields = NO_FIELDS; } if (externalizable) { cons = getExternalizableConstructor(cl); } else { cons = getSerializableConstructor(cl); // 其实就是writeObject方法 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; } .......107 } /** * Returns non-static private method with given signature defined by given * class, or null if none found. Access checks are disabled on the * returned method (if any). */ private static Method getPrivateMethod(Class cl, String name, Class[] argTypes, Class returnType) { try { Method meth = cl.getDeclaredMethod(name, argTypes); meth.setAccessible(true); int mods = meth.getModifiers(); return ((meth.getReturnType() == returnType) && ((mods & Modifier.STATIC) == 0) && ((mods & Modifier.PRIVATE) != 0)) ? meth : null; } catch (NoSuchMethodException ex) { return null; } } /** * Returns true if represented class is serializable (but not * externalizable) and defines a conformant writeObject method. Otherwise, * returns false. */ boolean hasWriteObjectMethod() { return (writeObjectMethod != null); } } public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { /** * Magic number that is written to the stream header. */ final static short STREAM_MAGIC = (short)0xaced; /** * Version number that is written to the stream header. */ final static short STREAM_VERSION = 5; public ObjectOutputStream(OutputStream out) throws IOException { verifySubclass(); bout = new BlockDataOutputStream(out); handles = new HandleTable(10, (float) 3.00); subs = new ReplaceTable(10, (float) 3.00); enableOverride = false; // 写入头信息 writeStreamHeader(); bout.setBlockDataMode(true); if (extendedDebugInfo) { debugInfoStack = new DebugTraceInfoStack(); } else { debugInfoStack = null; } } protected void writeStreamHeader() throws IOException { bout.writeShort(STREAM_MAGIC); bout.writeShort(STREAM_VERSION); } /** * Write the specified object to the ObjectOutputStream. The class of the * object, the signature of the class, and the values of the non-transient * and non-static fields of the class and all of its supertypes are * written. Default serialization for a class can be overridden using the * writeObject and the readObject methods. Objects referenced by this * object are written transitively so that a complete equivalent graph of * objects can be reconstructed by an ObjectInputStream.196 */ 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; } } /** * Underlying writeObject/writeUnshared implementation. */ private void writeObject0(Object obj, boolean unshared) throws IOException { boolean oldMode = bout.setBlockDataMode(false); depth++; try { // handle previously written and non-replaceable objects ...... // check for replacement object ......241 261 // if object replaced, run through original checks a second time ......279 // 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); } } 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); 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(); } } } /** * Writes instance data for each serializable class of given object, from * superclass to subclass. */ 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,则调用对应的方法进行写入,其实就是ObjectStreamClass 中的对应方法,能够得出序列化的第2条规则 if (slotDesc.hasWriteObjectMethod()) { PutFieldImpl oldPut = curPut; curPut = null; if (extendedDebugInfo) { debugInfoStack.push( "custom writeObject data (class \"" + slotDesc.getName() + "\")"); } SerialCallbackContext oldContext = curContext; 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); } } }
以上代码就是分析序列化状况2的实现,反序列化也能够一样跟踪发现,这里再也不重复。