Java中的序列化(Serializable)

简介

在做寒假的数据结构实验中,实验要求自己实现链表,这当然是非常简单的事情。但是在序列化存储的时候,发现如果简单地在链表类后面加上implements Serializable会出现StachOverFlow的问题。思考一番,一个链表在存储的时候会将自己后面的结点存储下来,然后再对下一个结点采取同样的处理,是会发生爆栈的问题。那究竟怎样处理这个问题呢?

关键字transient

通过阅读Java8中LinkedList的源码,能看到有一个以前没怎么见过的一个关键词transient。

transient是一个只能用来修饰实现了Serializable的变量或者基本数据类型的关键字,它表示被修饰的关键字将不会在序列化过程之中被序列化进去。

java序列化

引用自http://www.cnblogs.com/huhx/p/serializable.html

持久化的简单介绍

  “持久化”意味着对象的“生存时间”并不取决于程序是否正在执行——它存在或“生存”于程序的每一次调用之间。通过序列化一个对象,将其写入磁盘,以后在程序再次调用时重新恢复那个对象,就能圆满实现一种“持久”效果。

两种主要特性的支持

远程方法调用(RMI)使本来存在于其他机器的对象可以表现出好象就在本地机器上的行为。将消息发给远程对象时,需要通过对象序列化来传输参数和返回值。

使用一个Java Bean 时,它的状态信息通常在设计期间配置好。程序启动以后,这种状态信息必须保存下来,以便程序启动以后恢复;具体工作由对象序列化完成。

Serializable的一些说明

  1. 对象的序列化处理非常简单,只需对象实现了Serializable 接口即可(该接口仅是一个标记,没有方法)
  2. 序列化的对象包括基本数据类型,所有集合类以及其他许多东西,还有Class 对象
  3. 对象序列化不仅保存了对象的“全景图”,而且能追踪对象内包含的所有句柄并保存那些对象;接着又能对每个对象内包含的句柄进行追踪
  4. 使用transient关键字修饰的的变量,在序列化对象的过程中,该属性不会被序列化。

序列化的步骤

  1. 首先要创建某些OutputStream对象:OutputStream outputStream = new FileOutputStream(“output.txt”)
  2. 将其封装到ObjectOutputStream对象内:ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
  3. 此后只需调用writeObject()即可完成对象的序列化,并将其发送给OutputStream:objectOutputStream.writeObject(Object);
  4. 最后不要忘记关闭资源:objectOutputStream.close(), outputStream .close();

反序列化的步骤

  1. 首先要创建某些OutputStream对象:InputStream inputStream= new FileInputStream(“output.txt”)
  2. 将其封装到ObjectInputStream对象内:ObjectInputStream objectInputStream= new ObjectInputStream(inputStream);
  3. 此后只需调用writeObject()即可完成对象的反序列化:objectInputStream.readObject();
  4. 最后不要忘记关闭资源:objectInputStream.close(),inputStream.close();

实现

根据以上这些结论,我们可以来试着对自己的这个类来实现序列化。

第一步:添加transient

先将所有的变量都加上transient关键字,以便重写方法时不会出现其他问题;

1
2
3
private transient MyLinkedNode head;
private transient MyLinkedNode tail;
private transient int size;

第二步:writeObject方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// java.io.Serializable的写入函数
// 将LinkedList的“容量,所有的元素值”都写入到输出流中
private void writeObject(ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject();
// 写入“容量”
s.writeInt(size);
// 将链表中所有节点的数据都写入到输出流中
if(size != 0) {
for (MyLinkedNode e = head.next; e != null; e = e.next)
s.writeObject(e.data);
}
}

defaultWriteObject()这个方法是为了保证原有的Serializable的效果能正常运行。

If you use the default mechanism to write the non-transient parts of your object, you must call defaultWriteObject( ) as the first operation in writeObject( ), and defaultReadObject( ) as the first operation in readObject( ).
——Think in Java

然后我们只需要按照最简单的思路write数据即可。

第三步:readObject方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// 从输入流中读取“容量”
int size = s.readInt();
// 新建链表表头节点
head = new MyLinkedNode();
tail = head;
// 从输入流中将“所有的元素值”并逐个添加到链表中
if(size != 0) {
for (int i = 0; i < size; i++)
add((T) s.readObject());
}
}

readObject方法也是同理,按照反向构建链表即可。

总结

就这样我们实现了自定义类的Serializable序列化,可以发现它和Android中的自定义序列化的相似程度还是比较高的,都是按照一定的顺序写入,再按照一定的顺序读出。具体原理是什么,还只能等到将来再慢慢深入学习了,现在只希望能够课设多加几分(逃

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2018 Alex's Blog All Rights Reserved.

Yifeng Tang hält Urheberrechtsansprüche.