JAVA 对象的序列化

下面将介绍对象的序列化——一种将对象转成字节方便传送到别处或存储在硬盘上,并且再从转化成的字节重构对象的机制。

序列化是分布式管理必备的工具,分布式处理中将对象从一个虚拟传到另一个虚拟机。序列化也被用于故障转移和负载均衡方面,序列化对象可以从一个服务器移到另一个服务器。如果你开发过服务器端软件,就会经常需要序列化。下面介绍如何序列化。(摘自 《Core Java》)

 

一、简单的一个例子

Person.java

  1. import java.io.Serializable;
  2. /**
  3.  * @author 言曌
  4.  * @date 2018/3/5 下午11:23
  5.  */
  6. public class Person implements Serializable {
  7.     private String name;
  8.     private int age;
  9.     private String sex;
  10.     public Person(String name, int age, String sex) {
  11.         this.name = name;
  12.         this.age = age;
  13.         this.sex = sex;
  14.     }
  15.     @Override
  16.     public String toString() {
  17.         return "Person{" +
  18.                 "name='" + name + '\'' +
  19.                 ", age=" + age +
  20.                 ", sex='" + sex + '\'' +
  21.                 '}';
  22.     }
  23. }

 

TestObjSerializeAndDeserialize.java

  1. import java.io.*;
  2. /**
  3.  * @author 言曌
  4.  * @date 2018/3/5 下午11:24
  5.  */
  6. public class TestObjSerializeAndDeserialize {
  7.     public static void main(String[] args) throws Exception {
  8.         //1、序列化Person对象
  9.         SerializePerson();
  10.         //2、反序列Person对象
  11.         Person p = DeserializePerson();
  12.         System.out.println(p);
  13.     }
  14.     /**
  15.      * 序列化
  16.      * @throws FileNotFoundException
  17.      * @throws IOException
  18.      */
  19.     private static void SerializePerson() throws FileNotFoundException, IOException {
  20.         Person person = new Person("言曌",18,"男");
  21.         // ObjectOutputStream 对象输出流,将Person对象存储到指定路径下的Person.txt文件中,完成对Person对象的序列化操作
  22.         ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("/Users/liuyanzhao/Desktop/temp/Person.txt")));
  23.         oo.writeObject(person);
  24.         System.out.println("Person对象序列化成功!");
  25.         oo.close();
  26.     }
  27.     /**
  28.      * 反序列化
  29.      * @return
  30.      * @throws Exception
  31.      * @throws IOException
  32.      */
  33.     private static Person DeserializePerson() throws Exception, IOException {
  34.         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("/Users/liuyanzhao/Desktop/temp/Person.txt")));
  35.         Person person = (Person) ois.readObject();
  36.         System.out.println("Person对象反序列化成功!");
  37.         return person;
  38.     }
  39. }

 

1、我们可以先注释掉 main 方法里的反序列化的那两行代码,先执行序列化。然后就可以在指定目录,看到一个刚才创建的 Person.txt 文件。

使用 Sublime 打开以下

aced 0005 7372 0006 5065 7273 6f6e 6299
e8ea 3077 30c4 0200 0349 0003 6167 654c
0004 6e61 6d65 7400 124c 6a61 7661 2f6c
616e 672f 5374 7269 6e67 3b4c 0003 7365
7871 007e 0001 7870 0000 0012 7400 06e8
a880 e69b 8c74 0003 e794 b7

一堆十六进制编码。

 

2、然后注释掉序列化对象部分,运行反序列化部分。

运行效果图如下

JAVA 对象的序列化

 

 

二、为什么要手动设置 serialVersionUID

通常我们有时候在 Person 类里不写

private static final long serialVersionUID = 1L;

也能正常序列化和反序列化。

因为系统会自带帮我们创建一个 serialVersionUID。

下面测试一个例子,不设置 serialVersionUID ,当对象信息改变的时候,会出现什么状况。

 

1、先把序列化的那行注释掉,不进行序列化操作。使用刚才生成的 Person.txt

2、在 Person 里添加一个属性 phone

3、运行反序列化,会报一个异常

Exception in thread "main" java.io.InvalidClassException: Person; local class incompatible: stream classdesc serialVersionUID = 899743677678844260, local class serialVersionUID = -2823344428596659768
 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
 at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
 at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
 at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
 at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
 at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
 at TestObjSerializeAndDeserialize.DeserializePerson(TestObjSerializeAndDeserialize.java:43)
 at TestObjSerializeAndDeserialize.main(TestObjSerializeAndDeserialize.java:15)

 

因为在序列化的时候,将对象写入文件的时候,会写入类名和所有实例变量的名称和值。

其中 serialVersionUID 因为没有设置默认值,系统会自动根据哈希值生成一个。如果类的实现发生改变,那么 serialVersionUID 也会发生改变。

 

相反,如果我们在序列化之前加上

private static final long serialVersioLnUID = 1L;

 

然后序列化,然后给 Person 类加上一个 phone 字段。

这时候就不会报异常了。

JAVA 对象的序列化

 

 

至于 serialVersioLnUID 等于几并不重要,但是该属性的修饰和类型必须为 final long。

 

三、使用 transient 标记不需要序列化的字段

有些实例变量是不需要序列化的——例如当一个对象保留缓存值的时候,一般也不需要序列化该缓存值,重新计算缓存值而不是存储缓存值可能更好。

为了实现某些实例变量不序列化,简单的方法就是给这个变量添加一个 transient 修饰符,打过 transient 标记的字段在序列化的时候就会背忽略。

Person.java

  1. import java.io.Serializable;
  2. /**
  3.  * @author 言曌
  4.  * @date 2018/3/5 下午11:23
  5.  */
  6. public class Person implements Serializable {
  7.     private static final long serialVersioLnUID = 1L;
  8.     private String name;
  9.     private transient int age;
  10.     private  String sex;
  11.     private transient String phone;
  12.     public Person(String name, int age, String sex, String phone) {
  13.         this.name = name;
  14.         this.age = age;
  15.         this.sex = sex;
  16.         this.phone = phone;
  17.     }
  18.     @Override
  19.     public String toString() {
  20.         return "Person{" +
  21.                 "name='" + name + '\'' +
  22.                 ", age=" + age +
  23.                 ", sex='" + sex + '\'' +
  24.                 ", phone='" + phone + '\'' +
  25.                 '}';
  26.     }
  27. }

进行序列化和反序列化,效果图如下

JAVA 对象的序列化

其中 age 和 phone 是没有被序列化的,所有反序列化的时候也是没有值的。因为 age 是 int 类型,默认值是 0,而 phone 是 String 类型的,默认是 null。

 

 

 

参考:《Core Java for the Impatient》

本文地址:https://liuyanzhao.com/7649.html

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
言曌

发表评论

:?::razz::sad::evil::!::smile::oops::grin::eek::shock::???::cool::lol::mad::twisted::roll::wink::idea::arrow::neutral::cry::mrgreen: