序列化

转载 2016年05月31日 01:03:59

Java序列化机制和原理

 Java序列化算法透析

  Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。Java序列化API提供一种处理对象序列化的标准机制。在这里你能学到如何序列化一个对象,什么时候需要序列化以及Java序列化的算法,我们用一个实例来示范序列化以后的字节是如何描述一个对象的信息的。

序列化的必要性

Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。 
这就需要有一种可以在两端传输数据的协议。
Java序列化机制就是为了解决这个问题而产生。

如何序列化一个对象

一个对象能够序列化的前提是实现Serializable接口,Serializable接口没有方法,更像是个标记。 
有了这个标记的
Class就能被序列化机制处理。

import java.io.Serializable;   
class TestSerial implements Serializable {
public byte version = 100;
public byte count = 0;
}

然后我们写个程序将对象序列化并输出。ObjectOutputStream能把Object输出成Byte流。 
我们将
Byte流暂时存储到temp.out文件里。 

public static void main(String args[]) throws IOException {          
FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
TestSerial ts = new TestSerial();
oos.writeObject(ts);
oos.flush();
oos.close();
}

如果要从持久的文件中读取Bytes重建对象,我们可以使用ObjectInputStream  
public static void main(String args[]) throws IOException {     
FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream oin = new ObjectInputStream(fis);
TestSerial ts = (TestSerial) oin.readObject();
System.out.println("version="+ts.version);
}

执行结果为 100. 
对象的序列化格式
将一个对象序列化后是什么样子呢?打开刚才我们将对象序列化输出的temp.out文件
16进制方式显示。内容应该如下: 
AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65
73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05
63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78
70 00 64
这一坨字节就是用来描述序列化以后的TestSerial对象的,我们注意到TestSerial类中只有两个域:
public byte version = 100;
public byte count = 0;
且都是byte型,理论上存储这两个域只需要2byte,但是实际上temp.out占据空间为51bytes,也就是说除了数据以外,还包括了对序列化对象的其他描述
Java的序列化算法
序列化算法一般会按步骤做如下事情:
将对象实例相关的类元数据输出。
递归地输出类的超类描述直到不再有超类。
类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。
从上至下递归输出实例的数据
我们用另一个更完整覆盖所有可能出现的情况的例子来说明:
class parent implements Serializable {          
int parentVersion = 10;
}

class contain implements Serializable{          
Int containVersion = 11;
}

public class SerialTest extends parent implements Serializable {   
int version = 66;
contain con = new contain();
public int getVersion() {
return version;

}
public static void main(String args[]) throws IOException {
FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
SerialTest st = new SerialTest();
oos.writeObject(st);
oos.flush();
oos.close();
}
}
 
  1. AC ED: STREAM_MAGIC. 声明使用了序列化协议.
  2. 00 05: STREAM_VERSION. 序列化协议版本.
  3. 0x73: TC_OBJECT. 声明这是一个新的对象.  
  1. 0x72: TC_CLASSDESC. 声明这里开始一个新Class
  2. 00 0A: Class名字的长度.
  3. 53 65 72 69 61 6c 54 65 73 74: SerialTest,Class类名.
  4. 05 52 81 5A AC 66 02 F6: SerialVersionUID, 序列化ID,如果没有指定, 
    则会由算法随机生成一个
    8byteID.
  5. 0x02: 标记号. 该值声明该对象支持序列化。
  6. 00 02: 该类所包含的域个数。
  1. 0x49: 域类型. 49 代表"I", 也就是Int.
  2. 00 07: 域名字的长度.
  3. 76 65 72 73 69 6F 6E: version,域名字描述.
  1. 0x4C: 域的类型.
  2. 00 03: 域名字长度.
  3. 63 6F 6E: 域名字描述,con
  4. 0x74: TC_STRING. 代表一个new String.String来引用对象。
  5. 00 09: String长度.
  6. 4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, JVM的标准对象签名表示法.
  7. 0x78: TC_ENDBLOCKDATA,对象数据块结束的标志
  1. 0x72: TC_CLASSDESC. 声明这个是个新类.
  2. 00 06: 类名长度.
  3. 70 61 72 65 6E 74: parent,类名描述。
  4. 0E DB D2 BD 85 EE 63 7A: SerialVersionUID, 序列化ID.
  5. 0x02: 标记号. 该值声明该对象支持序列化.
  6. 00 01: 类中域的个数.
  1. 0x49: 域类型. 49 代表"I", 也就是Int.
  2. 00 0D: 域名字长度.
  3. 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion,域名字描述。
  4. 0x78: TC_ENDBLOCKDATA,对象块结束的标志。
  5. 0x70: TC_NULL, 说明没有其他超类的标志。.
  1. 00 00 00 0A: 10, parentVersion域的值.
  1. 00 00 00 42: 66, version域的值.
  1. 0x73: TC_OBJECT, 声明这是一个新的对象.
  2. 0x72: TC_CLASSDESC声明这里开始一个新Class.
  3. 00 07: 类名的长度.
  4. 63 6F 6E 74 61 69 6E: contain,类名描述.
  5. FC BB E6 0E FB CB 60 C7: SerialVersionUID, 序列化ID.
  6. 0x02: Various flags. 标记号. 该值声明该对象支持序列化
  7. 00 01: 类内的域个数。
  1. 0x49: 域类型. 49 代表"I", 也就是Int..
  2. 00 0E: 域名字长度.
  3. 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, 域名字描述.
  4. 0x78: TC_ENDBLOCKDATA对象块结束的标志.
  1. 0x70:TC_NULL,没有超类了。
  1. 00 00 00 0B: 11, containVersion的值.

这个例子是相当的直白啦。SerialTest类实现Parent超类,内部还持有一个Container对象。

序列化后的格式如下:

AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65

73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07

76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09

4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72

65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00

0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70

00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74

61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00

0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78

70 00 00 00 0B

我们来仔细看看这些字节都代表了啥。开头部分,见颜色

序列化算法的第一步就是输出对象相关类的描述。例子所示对象为SerialTest类实例, 
因此接下来输出
SerialTest类的描述。见颜色

接下来,算法输出其中的一个域,int version=66;见颜色

然后,算法输出下一个域,contain con = new contain();这个有点特殊,是个对象。 
描述对象类型引用时需要使用
JVM的标准对象签名表示法,见颜色

.接下来算法就会输出超类也就是Parent类描述了,见颜色

下一步,输出parent类的域描述,int parentVersion=100;同见颜色

到此为止,算法已经对所有的类的描述都做了输出。下一步就是把实例对象的实际值输出了。这时候是从parent Class的域开始的,见颜色

还有SerialTest类的域:

再往后的bytes比较有意思,算法需要描述contain类的信息,要记住, 
现在还没有对
contain类进行过描述,见颜色

.输出contain的唯一的域描述,int containVersion=11

这时,序列化算法会检查contain是否有超类,如果有的话会接着输出。

最后,将contain类实际域值输出。

OK,我们讨论了java序列化的机制和原理,希望能对同学们有所帮助。

serialVersionUID值的重要作用
          根据上面的分析,可以发现如果一个类可序列化,serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败.
注意问题
如果序列化时代码这样写:
SerialTest st = new SerialTest(); 
oos.writeObject((parent)st);

会发现序列化的对象依然是SerialTest,如果在分布式环境中用Parent反序列化(调用段不存在SerialTest),会造成ClassNotFoundException.

序列化例子

 对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。

  对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2) 通过对象输入流的readObject()方法读取对象。

对象序列化和反序列范例:

  定义一个Person类,实现Serializable接口


1   import java.io.Serializable;
10 public class Person implements Serializable {
11 
12     /**
13      * 序列化ID
14      */
15     private static final long serialVersionUID = -5809782578272943999L;
16     private int age;
17     private String name;
18     private String sex;
19 
20     public int getAge() {
21         return age;
22     }
23 
24     public String getName() {
25         return name;
26     }
27 
28     public String getSex() {
29         return sex;
30     }
31 
32     public void setAge(int age) {
33         this.age = age;
34     }
35 
36     public void setName(String name) {
37         this.name = name;
38     }
39 
40     public void setSex(String sex) {
41         this.sex = sex;
42     }
43 }
  序列化和反序列化Person类对象

 1 import java.io.File;
 2 import java.io.FileInputStream;
 3 import java.io.FileNotFoundException;
 4 import java.io.FileOutputStream;
 5 import java.io.IOException;
 6 import java.io.ObjectInputStream;
 7 import java.io.ObjectOutputStream;
 8 import java.text.MessageFormat;
 9 
10 /**
11  * <p>ClassName: TestObjSerializeAndDeserialize<p>
12  * <p>Description: 测试对象的序列化和反序列<p>
13  * @author xudp
14  * @version 1.0 V
15  * @createTime 2014-6-9 下午03:17:25
16  */
17 public class TestObjSerializeAndDeserialize {
18 
19     public static void main(String[] args) throws Exception {
20         SerializePerson();//序列化Person对象
21         Person p = DeserializePerson();//反序列Perons对象
22         System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
23                                                  p.getName(), p.getAge(), p.getSex()));
24     }
25     
26     /**
27      * MethodName: SerializePerson 
28      * Description: 序列化Person对象
29      * @author xudp
30      * @throws FileNotFoundException
31      * @throws IOException
32      */
33     private static void SerializePerson() throws FileNotFoundException,
34             IOException {
35         Person person = new Person();
36         person.setName("gacl");
37         person.setAge(25);
38         person.setSex("男");
39         // ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
40         ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
41                 new File("E:/Person.txt")));
42         oo.writeObject(person);
43         System.out.println("Person对象序列化成功!");
44         oo.close();
45     }
46 
47     /**
48      * MethodName: DeserializePerson 
49      * Description: 反序列Perons对象
50      * @author xudp
51      * @return
52      * @throws Exception
53      * @throws IOException
54      */
55     private static Person DeserializePerson() throws Exception, IOException {
56         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
57                 new File("E:/Person.txt")));
58         Person person = (Person) ois.readObject();
59         System.out.println("Person对象反序列化成功!");
60         return person;
61     }
62 
63 }

指定序列化id的姿势

class Customer implements Serializable {
 2     /**
 3      * Customer类中定义的serialVersionUID(序列化版本号)
 4      */
 5     private static final long serialVersionUID = -5182532647273106745L;
 6     private String name;
 7     private int age;



序列化、反序列化到底是什么?

一、序列化、反序列化到底是什么? (1) 名词解释 对象的序列化 : 把Java对象转换为字节序列并存储至一个储存媒介的过程。 对象的反序列化:把字节序列恢复为Java对象的过程。 (2)...
  • Nicholas_Nick
  • Nicholas_Nick
  • 2016年03月30日 10:12
  • 3831

序列化和反序列化的简单理解

一、序列化和反序列化的概念   把对象转换为字节序列的过程称为对象的序列化。   把字节序列恢复为对象的过程称为对象的反序列化。   对象的序列化主要有两种用途:   1) 把对象的字节序列永久地保...
  • abc6368765
  • abc6368765
  • 2016年05月10日 19:24
  • 31297

Android中两种序列化方式的比较Serializable和Parcelable

Serializable和Parcelable接口可以完成对象的序列化过程,当我们需要通过Intent和Binder传输数据时就需要使用者两种序列化方式。还有,我们需要对象持久化到存储设备或者通过网络...
  • wangchunlei123
  • wangchunlei123
  • 2016年05月08日 13:33
  • 10334

二叉树的序列化和反序列化实现(C++)

题目: 实现二叉树的序列化和反序列化的函数。 思路:         二叉树的序列化 即给定二叉树的头指针,按照某种遍历方式将所有结点链接为一个数组或字符串(/指针)。        ...
  • u013575812
  • u013575812
  • 2015年12月05日 14:39
  • 1199

java,什么是序列化,怎么实现序列化

转自:http://zhidao.baidu.com/question/202556575.html?qbl=relate_question_0&word=%CA%B2%C3%B4%CA%C7java...
  • zhangzhangjava
  • zhangzhangjava
  • 2016年04月12日 17:33
  • 3221

Java序列化,反射,注解(一)

序列化: 什么是序列化,为什么要做序列化,考虑一种场景,考虑这种应用发生在要对一个对象做永久性保存的时候, 可以把它保存到电脑磁盘,也可以保存到数据库。而在你需要用到这个对象的时候可以从磁盘或数据库...
  • chaogu94
  • chaogu94
  • 2016年09月02日 17:43
  • 1180

google protobuf入门(序列化和反序列化)

ProtoBuf,全称是Protocol Buffers, 它是谷歌内部用的一种高效的、可扩展的对结构化数据进行编码的格式规范。谷歌自己内部很多程序之间的通信协议都用了ProtoBuf。 下面介绍的是...
  • paul342
  • paul342
  • 2016年05月19日 17:09
  • 4693

Spring Data Redis提供的几种序列化的比较

* redis虽然提供了对list set hash等数据类型的支持,但是没有提供对POJO对象的支持,底层都是把对象序列化后再以字符串的方式存储的。* Spring data提供了若干个Seria...
  • CSDN_XueXiaoQiang
  • CSDN_XueXiaoQiang
  • 2017年06月23日 17:05
  • 1179

java中为什么要实现序列化,什么时候实现序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,将数据分解成字节流,以便存储在文件中或在网络上传输。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是...
  • tlycherry
  • tlycherry
  • 2013年05月28日 22:19
  • 10482

Java中List或者HashMap对象-序列化和反序列化并附读写文件操作

关于常用的序列化工具可以看下文章 浅析若干Java序列化工具 闲言少叙,直接上代码。 Json格式的序列化和反序列化使用alibaba的Fastjson,Maven项目需要引用包 com....
  • lcanghai
  • lcanghai
  • 2017年04月06日 19:21
  • 4126
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:序列化
举报原因:
原因补充:

(最多只允许输入30个字)