背景
实现Serializable接口就能把对象序列化反序列化出去,那么原理是什么呢?
带着以下问题一探究竟  
- 为什么实现这个接口就能序列化和反序列化呢?
- 序列化和反序列化的内容是什么呢,可以自定义吗?
为什么实现这个接口就能序列化和反序列化呢
- java序列化代码  1 
 2
 3
 4
 5
 6
 7
 8
 9public static void main(String[] args) throws IOException { 
 Object o = new Serializable() {
 String strValue = "这是内容";
 Integer intValue = 999;
 };
 File file = new File(System.getProperty("java.io.tmpdir"), "serializable/");
 ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
 objectOutputStream.writeObject(o);
 }
这样就会把对象序列化到指定的文件中,我们点开writeObject方法看源码一探究竟
| 1 | 
 | 
所以要想[反]序列化对象,类型必须是String|array|enum|Serializable|class,不然就会直接报错
- writeOrdinaryObject - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21- private void writeOrdinaryObject(Object obj, 
 ObjectStreamClass desc,
 boolean unshared)
 throws IOException
 {
 try {
 bout.writeByte(TC_OBJECT);
 writeClassDesc(desc, false);
 handles.assign(unshared ? null : obj);
 // 实现java.io.Externalizable接口,可自定义[反]序列化的内容
 if (desc.isExternalizable() && !desc.isProxy()) {
 writeExternalData((Externalizable) obj);
 } else {
 writeSerialData(obj, desc);
 }
 } finally {
 if (extendedDebugInfo) {
 debugInfoStack.pop();
 }
 }
 }
- writeSerialData - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40- 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;
 // 当前对象[反]序列化工具类的包装,
 // 如果没有实现java.io.Externalizable自定义[反]序列化接口也没关系
 // 直接把writeObject(序列化)和readObject(反序列化)名称写在你自己的类当中,也可以自定义[反]序列化的内容哦
 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);
 }
 }
 }- 如果类中有writeReplace方法,则会调用,并且序列化的目标类为该方法的返回值 
- defaultWriteFields - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40- 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++) {
 if (extendedDebugInfo) {
 debugInfoStack.push(
 "field (class \"" + desc.getName() + "\", name: \"" +
 fields[numPrimFields + i].getName() + "\", type: \"" +
 fields[numPrimFields + i].getType() + "\")");
 }
 try {
 // 继续递归调用
 writeObject0(objVals[i],
 fields[numPrimFields + i].isUnshared());
 } finally {
 if (extendedDebugInfo) {
 debugInfoStack.pop();
 }
 }
 }
 }
总结
- 除基本类型和String、Enum、class和array外,其他类型想要[反]序列化,必须实现Serializable接口,不然直接报错 
- 自定义[反]序列化内容有两种方法,一是直接写方法(writeObject(序列化)|readObject(反序列化))到自己的类中,二是实现java.io.Externalizable接口. 
 writeObject参数类型是ObjectOutputStream
 readObject参数类型是ObjectInputStream
 方法返回类型必须为void,private,非static
- writeReplace返回值为Object类型的方法,可自定义真正序列化的对象 
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 老曹的私房站!
 评论
