什么是对象序列化
- 对象序列化就是将内存中的java对象状态组织成一组字节,并且可以将这个数据写入到本地磁盘或者网络IO中,而且在使用这个对象时就可以根据这个数据恢复出之前的那个对象的状态,这叫反序列化。一般在JVM停止运行后,用于持久化对象到磁盘当中;或者进行远程方法调用。
- 由于是保存实例对象的状态,所以序列化的只是保存对象的除了静态成员的所有成员变量
关于java.io.Serializable接口
只要在定义类的时候定义了此接口,就实现了可序列化的语义,并不需要在类体内做多余的操作,此时实现的是默认对象序列化机制。
transient关键字
当类的某个域(字段)被标注成transient,则默认序列化机制就会忽略该字段,一般用于忽略敏感数据,或者忽略对序列化性能消耗较大的字段。
自定义序列化
如果不想使用默认序列化实现,可以在实现Serialable接口的类中自定义两个函数
- private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException
- ** private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException**
在自定义的writeObject方法可以将标注为transient关键字的字段进行序列化
自定义的方法是private方法,所以是通过反射调用的,详情见ObjectOutputStream.writeSerialData()和ObjectInputStream.readSerialData()这两个方法
接下来我们来分析一下ArrayList的自定义序列化实现
/**
* 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 size as capacity for behavioural compatibility with clone()
s.writeInt(size); // 将列表size作为列表的容量写入到ObjectOutputStream
// 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 {
elementData = EMPTY_ELEMENTDATA; // 初始化空数组
// Read in size, and any hidden stuff
s.defaultReadObject(); // 执行默认反序列化,会将此类的实例对象序列化的域恢复
// Read in capacity
s.readInt(); // ignored 忽略序列化的容量(以size作为容量)
if (size > 0) {
// 根据默认反序列出来的size大小确定分配的空间,而不是根据容量
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size); //确保容量不小于size的大小
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject(); // 反序列缓冲数组
}
}
}
JDK 序列化步骤
ArrayList<String> a = new ArrayList<String>();
// 1. 创建对象输出流对象,可以包装其他类型的目标输出流,如 FileOutputStream
FileOutputStream fos = new FileOutputStream("D:\\objectfile.obj");
ObjectOutputStream out = new ObjectOutputStream(fos);
// 2. 写入对象到输出流
out.writeObject(a);
// 3. 写入其他对象到输出流
out.writeObject("finished");
// 4. 冲刷缓冲区
out.flush();
// 5. 关闭IO流对象
out.close();
fos.close();
JDK 反序列化步骤
// 1. 创建对象输入流对象,可以包装其他类型的目标输入流,如 FileInputStream
FileInputStream fis = new FileInputStream("D:\\objectfile.obj");
ObjectInputStream in = new ObjectInputStream(fis);
// 2. 读取输入流中的对象
ArrayList<String> b = (ArrayList<String>)in.readObject();
// 3. 读取输入流中的其他对象
String status = (String)in.readObject();
// 4. 关闭IO流对象
in.close();
fis.close();
注意:写入和读取的顺序需要一致,按顺序写入,并按顺序读取;而且对象文件编码方式要一致