Java中主要对象实现了java.io.Serializable 就可以进行序列化
Java IO 包中为我们提供了 ObjectInputStream 和 ObjectOutputStream 两个类。 java.io.ObjectOutputStream 类实现类的序列化功能。 java.io.ObjectInputStream 类实现了反序列化功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Test{ public static void main(String[] args) throws Exception { File file = new File("d:\\a.user"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); User user1 = new User(); user1.setUserName("zhangsan"); user1.setPassword("123456"); user1.setAddr("北京中关村"); oos.writeObject(user1); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); User user2 = (User)ois.readObject(); System.out.println(user2); } }
|
1 2 3 4
| 输出结果: User [userName=zhangsan, password=123456, addr=北京中关村] 使用 ObjectOutputStream 把 user1 实例序列化到 d:\user 文件中。 使用 ObjectInputStream 把 d:\user 文件中的数据反序列化成 user2 实,并打印。
|
在序列化过程中,如果被序列化的类中定义了writeObject 和 readObject 方法,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化。
如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。
用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。
如果一个类中包含了writeObject 和writeObject方法,这两个方法如何调用?
答案是在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法时,会通过反射的方式调用。
ObjectOutputStream 原理解析
writeObject 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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; } }
|
1、通过 enableOverride 判断是否执行 writeObjectOverride() 方法。
2、调用writeObject() 方法。
writeObject0 方法
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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| private void writeObject0(Object obj, boolean unshared) throws IOException { boolean oldMode = bout.setBlockDataMode(false); depth++; try { int h; if ((obj = subs.lookup(obj)) == null) { writeNull(); return; } else if (!unshared && (h = handles.lookup(obj)) != -1) { writeHandle(h); return; } else if (obj instanceof Class) { writeClass((Class) obj, unshared); return; } else if (obj instanceof ObjectStreamClass) { writeClassDesc((ObjectStreamClass) obj, unshared); return; }
Object orig = obj; Class cl = obj.getClass(); ObjectStreamClass desc; for (;;) { Class repCl; desc = ObjectStreamClass.lookup(cl, true); if (!desc.hasWriteReplaceMethod() || (obj = desc.invokeWriteReplace(obj)) == null || (repCl = obj.getClass()) == cl) { break; } cl = repCl; } if (enableReplace) { Object rep = replaceObject(obj); if (rep != obj && rep != null) { cl = rep.getClass(); desc = ObjectStreamClass.lookup(cl, true); } obj = rep; }
if (obj != orig) { subs.assign(orig, obj); if (obj == null) { writeNull(); return; } else if (!unshared && (h = handles.lookup(obj)) != -1) { writeHandle(h); return; } else if (obj instanceof Class) { writeClass((Class) obj, unshared); return; } else if (obj instanceof ObjectStreamClass) { writeClassDesc((ObjectStreamClass) obj, unshared); return; } } ************** 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); } }
|
1、 如果该对象是String、数组、枚举类型的,调用相应的方法进行写入。
2、如果对象是 Serializable 的,则调用 writeOrdinaryObject() 方法,该方法是序列化的核心方法 。在这里我们终于看到 Serializable的作用了。因为Serializable中没有定义方法,只是起到标识作用,该标识作用就在这提现。
writeOrdinaryObject 方法
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
| 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(); } } }
|
1、检查是否可以序列化
2、写入类型
3、写 class 的描述信息
4、判断是 该对象 否实现了 Externalizable 接口
- 如果实现了则调用 writeExternalData 方法。
- 如果没有实现则调用 writeSerialData 方法。
writeSerialData 方法
实现Serializable 序列化接口
1、判断该类是否定义了 writeObject() 方法,如果定义了,则通过反射调用该对象的 writeObject() 方法,执行我们自己定义的序列化规则。
2、没有定义writeObject() 方法,则调用 defaultWriteFields() 方法执行默认的序列化规则。
我们平常在重写 writeObject() 方法的时候一般也会先调用 defaultWriteFields() 方法的,然后在写上其它特殊的序列化。
ArrayList 的序列化
ArrayList实现了java.io.Serializable
接口,那么我们就可以对它进行序列化及反序列化。因为elementData是transient
的,所以我们认为这个成员变量不会被序列化而保留下来。 ArrayList底层是通过数组实现的。那么数组elementData
其实就是用来保存列表中的元素的。通过该属性的声明方式我们知道,他是无法通过序列化持久化下来的。那么为什么通过序列化和反序列化把List中的元素保留下来了呢?
还是因为ArrayList 中定义了writeObject 方法和WriteObject 方法
为什么ArrayList 要自定义序列化方式呢?
ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那就会序列化99个null元素。为了保证在序列化的时候不会将这么多null同时进行序列化,ArrayList把元素数组设置为transient。
前面说过,为了防止一个包含大量空对象的数组被序列化,为了优化存储,所以,ArrayList使用transient
来声明elementData
。 但是,作为一个集合,在序列化过程中还必须保证其中的元素可以被持久化下来,所以,通过重写writeObject
和 readObject
方法的方式把其中的元素保留下来。
writeObject
方法把elementData
数组中的元素遍历的保存到输出流(ObjectOutputStream)中。
readObject
方法从输入流(ObjectInputStream)中读出对象并保存赋值到elementData
数组中。
https://www.jianshu.com/p/af2f0a4b03b5
https://www.jianshu.com/p/3e3d86716f76