基本概念

  • 把对象转换为字节序列的过程称为对象的序列化。
  • 把字节序列恢复为对象的过程称为对象的反序列化。
  • 对象的序列化主要有两种用途:

    • 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
    • 在网络上传送对象的字节序列。

      JDK类库中的序列化API

  • java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

  • java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

只有实现了SerializableExternalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式 。

 对象序列化包括如下步骤:

  • 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  • 通过对象输出流的writeObject()方法写对象。

 对象反序列化的步骤如下:

  • 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  • 通过对象输入流的readObject()方法读取对象。

定义一个Person类实现Serializable接口

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

序列化和反序列化代码

  1. public class TestObjSerializeAndDeserialize {
  2. 18
  3. 19 public static void main(String[] args) throws Exception {
  4. 20 SerializePerson();//序列化Person对象
  5. 21 Person p = DeserializePerson();//反序列Perons对象
  6. 22 System.out.println(MessageFormat.format("name={0},age={1},sex={2}",
  7. 23 p.getName(), p.getAge(), p.getSex()));
  8. 24 }
  9. 25
  10. 33 private static void SerializePerson() throws FileNotFoundException,
  11. 34 IOException {
  12. 35 Person person = new Person();
  13. 36 person.setName("gacl");
  14. 37 person.setAge(25);
  15. 38 person.setSex("男");
  16. 39 // ObjectOutputStream 对象输出流,
  17. //将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
  18. //包装一个输出流对象
  19. 40 ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(
  20. 41 new File("E:/Person.txt")));
  21. 42 oo.writeObject(person);
  22. 43 System.out.println("Person对象序列化成功!");
  23. 44 oo.close();
  24. 45 }
  25. 46
  26. 47
  27. 55 private static Person DeserializePerson() throws Exception, IOException {
  28. 56 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
  29. 57 new File("E:/Person.txt")));
  30. 58 Person person = (Person) ois.readObject();
  31. 59 System.out.println("Person对象反序列化成功!");
  32. 60 return person;
  33. 61 }
  34. 62
  35. 63 }

SerialversionUID的作用

如果不显式添加SerialversionUID,java编译器通过摘要算法给这个类默认生成一个UID,class文件即使多一个空格生成的UID也会不同。
当序列化一个对象后,对类进行了修改(添加属性或者方法),再反序列化时会报UID不一致的错误,而显式声明UID后反序列化得到的对象与修改后的类是一致的。

开源社区序列化API

java中实现的序列化效率是极低的,在小项目中使用还尚可,但对于高并发,对序列化速度要求比较高的项目是将会成为瓶颈问题,目前常见的一些序列化工具都比其效率高。

  1. json/xml

目前使用比较频繁的格式化数据工具,简单直观,可读性好,有jackson,gson,fastjson等等,效率比java原生的序列化快2到4倍。

  1. kryo

是一个快速序列化/反序列化工具,效率比java高出一个级别,序列化出来的结果,是其自定义的、独有的一种格式,体积更小,一般只用来进行序列化和反序列化,而不用于在多个系统、甚至多种语言间进行数据交换(目前 kryo 也只有 java 实现),目前已经有多家大公司使用,相对比较稳定。

  1. fst

与kryo类似,是apache组织的一个开源项目,完全兼容JDK序列化协议的系列化框架,序列化速度大概是JDK的4-10倍,大小是JDK大小的1/3左右。

  1. protostuff

是google在原来的protobuffer上的优化产品。使用起来也比较简单易用,目前效率也是最好的一种序列化工具。

reference