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

来源:互联网 发布:找客源软件 编辑:程序博客网 时间:2024/06/04 08:40

一、序列化和反序列化的概念

  • 序列化: 将数据结构或对象转换成二进制串的过程
  • 反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程


  把对象转换为字节序列的过程称为对象的序列化
  把字节序列恢复为对象的过程称为对象的反序列化

  对象的序列化主要有两种用途:
  1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。

  在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

  当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

二、JDK类库中的序列化API

  java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
  只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
  对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。

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

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

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

复制代码
 1 import java.io.Serializable; 2  3 /** 4  * <p>ClassName: Person<p> 5  * <p>Description:测试对象序列化和反序列化<p> 6  * @author xudp 7  * @version 1.0 V 8  * @createTime 2014-6-9 下午02:33:25 9  */10 public class Person implements Serializable {11 12     /**13      * 序列化ID14      */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 xudp14  * @version 1.0 V15  * @createTime 2014-6-9 下午03:17:2516  */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 xudp30      * @throws FileNotFoundException31      * @throws IOException32      */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 xudp51      * @return52      * @throws Exception53      * @throws IOException54      */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 }
复制代码

代码运行结果如下:

序列化Person成功后在E盘生成了一个Person.txt文件,而反序列化Person是读取E盘的Person.txt后生成了一个Person对象

 三、serialVersionUID的作用

  s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

1 private static final long serialVersionUID

  实现Serializable接口的类如果类中没有添加serialVersionUID,那么就会出现如下的警告提示

  

  用鼠标点击就会弹出生成serialVersionUID的对话框,如下图所示:

  

  serialVersionUID有两种生成方式:

  采用这种方式生成的serialVersionUID是1L,例如:

1 private static final long serialVersionUID = 1L;

  采用这种方式生成的serialVersionUID是根据类名,接口名,方法和属性等来生成的,例如:

1 private static final long serialVersionUID = 4603642343377807741L;

  添加了之后就不会出现那个警告提示了,如下所示:

  

  扯了那么多,那么serialVersionUID(序列化版本号)到底有什么用呢,我们用如下的例子来说明一下serialVersionUID的作用,看下面的代码:

复制代码
 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.io.Serializable; 9 10 public class TestSerialversionUID {11 12     public static void main(String[] args) throws Exception {13         SerializeCustomer();// 序列化Customer对象14         Customer customer = DeserializeCustomer();// 反序列Customer对象15         System.out.println(customer);16     }17 18     /**19      * MethodName: SerializeCustomer 20      * Description: 序列化Customer对象21      * @author xudp22      * @throws FileNotFoundException23      * @throws IOException24      */25     private static void SerializeCustomer() throws FileNotFoundException,26             IOException {27         Customer customer = new Customer("gacl",25);28         // ObjectOutputStream 对象输出流29         ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(30                 new File("E:/Customer.txt")));31         oo.writeObject(customer);32         System.out.println("Customer对象序列化成功!");33         oo.close();34     }35 36     /**37      * MethodName: DeserializeCustomer 38      * Description: 反序列Customer对象39      * @author xudp40      * @return41      * @throws Exception42      * @throws IOException43      */44     private static Customer DeserializeCustomer() throws Exception, IOException {45         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(46                 new File("E:/Customer.txt")));47         Customer customer = (Customer) ois.readObject();48         System.out.println("Customer对象反序列化成功!");49         return customer;50     }51 }52 53 /**54  * <p>ClassName: Customer<p>55  * <p>Description: Customer实现了Serializable接口,可以被序列化<p>56  * @author xudp57  * @version 1.0 V58  * @createTime 2014-6-9 下午04:20:1759  */60 class Customer implements Serializable {61     //Customer类中没有定义serialVersionUID62     private String name;63     private int age;64 65     public Customer(String name, int age) {66         this.name = name;67         this.age = age;68     }69 70     /*71      * @MethodName toString72      * @Description 重写Object类的toString()方法73      * @author xudp74      * @return string75      * @see java.lang.Object#toString()76      */77     @Override78     public String toString() {79         return "name=" + name + ", age=" + age;80     }81 }
复制代码

运行结果:

序列化和反序列化都成功了。

下面我们修改一下Customer类,添加多一个sex属性,如下:

复制代码
 1 class Customer implements Serializable { 2     //Customer类中没有定义serialVersionUID 3     private String name; 4     private int age; 5  6     //新添加的sex属性 7     private String sex; 8      9     public Customer(String name, int age) {10         this.name = name;11         this.age = age;12     }13     14     public Customer(String name, int age,String sex) {15         this.name = name;16         this.age = age;17         this.sex = sex;18     }19 20     /*21      * @MethodName toString22      * @Description 重写Object类的toString()方法23      * @author xudp24      * @return string25      * @see java.lang.Object#toString()26      */27     @Override28     public String toString() {29         return "name=" + name + ", age=" + age;30     }31 }
复制代码

  然后执行反序列操作,此时就会抛出如下的异常信息:

1 Exception in thread "main" java.io.InvalidClassException: Customer; 2 local class incompatible: 3 stream classdesc serialVersionUID = -88175599799432325, 4 local class serialVersionUID = -5182532647273106745

  意思就是说,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID。在TestSerialversionUID例子中,没有指定Customer类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用

  下面继续修改Customer类,给Customer指定一个serialVersionUID,修改后的代码如下:

复制代码
 1 class Customer implements Serializable { 2     /** 3      * Customer类中定义的serialVersionUID(序列化版本号) 4      */ 5     private static final long serialVersionUID = -5182532647273106745L; 6     private String name; 7     private int age; 8  9     //新添加的sex属性10     //private String sex;11     12     public Customer(String name, int age) {13         this.name = name;14         this.age = age;15     }16     17     /*public Customer(String name, int age,String sex) {18         this.name = name;19         this.age = age;20         this.sex = sex;21     }*/22 23     /*24      * @MethodName toString25      * @Description 重写Object类的toString()方法26      * @author xudp27      * @return string28      * @see java.lang.Object#toString()29      */30     @Override31     public String toString() {32         return "name=" + name + ", age=" + age;33     }34 }
复制代码

  重新执行序列化操作,将Customer对象序列化到本地硬盘的Customer.txt文件存储,然后修改Customer类,添加sex属性,修改后的Customer类代码如下:

复制代码
 1 class Customer implements Serializable { 2     /** 3      * Customer类中定义的serialVersionUID(序列化版本号) 4      */ 5     private static final long serialVersionUID = -5182532647273106745L; 6     private String name; 7     private int age; 8  9     //新添加的sex属性10     private String sex;11     12     public Customer(String name, int age) {13         this.name = name;14         this.age = age;15     }16     17     public Customer(String name, int age,String sex) {18         this.name = name;19         this.age = age;20         this.sex = sex;21     }22 23     /*24      * @MethodName toString25      * @Description 重写Object类的toString()方法26      * @author xudp27      * @return string28      * @see java.lang.Object#toString()29      */30     @Override31     public String toString() {32         return "name=" + name + ", age=" + age;33     }34 }
复制代码

执行反序列操作,这次就可以反序列成功了,如下所示:

  

四、serialVersionUID的取值

  serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
  类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值

  显式地定义serialVersionUID有两种用途:
    1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
    2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 dw玫瑰金手表褪色怎么办 dw金色表带黑了怎么办 机械表机芯坏了怎么办 银镀玫瑰金褪色怎么办 苹果七p玫瑰金掉漆怎么办 美度镀金表掉色怎么办 吃了发黑的香菇怎么办 脸上的皮肤暗黄怎么办 吃了变黑的香菇怎么办 怀孕喝了玫瑰茶怎么办 孕妇爱喝茉莉茶怎么办 干菊花生虫了该怎么办 黑枸杞放了两年怎么办 来大姨妈喝茶了怎么办 干柠檬片过期了怎么办 山地玫瑰根黑了怎么办 带18k的金过敏怎么办 18k钻戒断了怎么办 18k钻戒戒托变色怎么办 苹果七中间按键脱漆怎么办 苹果手机屏幕边缘掉漆了怎么办 十八k白金变色了怎么办 手机后面掉漆了怎么办 玫瑰金戒指刮花了怎么办 玫瑰金手镯刮花了怎么办 玫瑰金链子断了怎么办 dw玫瑰金掉色了怎么办 6s玫瑰金氧化了怎么办 书本湿的很严重怎么办 除湿盒的水满了怎么办 书被水泡变黄了怎么办 文件纸被弄皱了怎么办 纸的一角皱了怎么办 裤子被雨水打湿变色了怎么办 书本上粘了有油怎么办 手被水泡了皱了怎么办 水泡破了感染了怎么办 夏天腋下出汗会弄湿衣服怎么办 夏天腋下出汗多弄湿衣服怎么办 上眼皮干燥起皮怎么办 怀孕下面湿又痒怎么办