首页 > 代码库 > Java序列化、反序列化和单例模式

Java序列化、反序列化和单例模式

学习JAVA的时候,特别是涉及到网络编程时,我们时常让我们的实体类实现一个接口

public class Entity implements Serializable{}

这样子我们可以通过输入输出流ObjectOutputStream和ObjectInputStream写入或读取该对象。所以,简单来说,序列化就是把对象转换为字节数据流,反序列化就是把字节序列流转成相应的Java对象。使用序列化的最常用的两个场景:

1、需要把对象信息存储到磁盘文件中的时候,比如大量用户登录的Session信息

2、需要把对象信息通过网络传送的时候,需要传送字节流

实现java.io.Serializable接口之后,编辑器会给出警告,让你给出一个属性的定义:

private static final long serialVersionUID = 1L;

我们可以简单的认为,这是一个“类版本”的表示,不同的编辑器可能会对同一个类给出不同的值,类文件的细小改动也可能会产生不同的值,因为这个值在类变更之后起到了代码兼容的作用,一般规则如下:

1、两个不同版本的类,如果serialVersionUID相同,则说明反序列化兼容,以现有版本为模板反序列化

2、两个不同版本的类,如果serialVersionUID不同,说明反序列化不兼容,反序列化会失败

因此,我们的开发过程中,建议显示定义该值,并给以明确的定义,以方便兼容性判断。

下面代码示例,首先定义一个可序列化的实体对象:

package com.minlz.serialize;import java.io.Serializable;/** *  @author minliangzhi *  @date 2016年8月30日 */public class Person implements Serializable {    private static final long serialVersionUID = 20160830001L;        private String name;        private int age;        public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }        @Override    public String toString() {        return "Person [name=" + name + ", age=" + age + "]";    }}

然后给出一个序列化和反序列化的方法:

    public static void main(String[] args) throws Exception {        Person p1 = new Person();        p1.setName("Bruce Min");        p1.setAge(25);        System.out.println("p1: " + p1.toString());        /** 序列化 */        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();        ObjectOutputStream objOut = new ObjectOutputStream(byteOut);        objOut.writeObject(p1);                /** 反序列化 */        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());        ObjectInputStream objIn = new ObjectInputStream(byteIn);        Person p2 = (Person)objIn.readObject();        System.out.println("p2: " + p2.toString());    }

显示的结果是:

p1: Person [name=Bruce Min, age=25]p2: Person [name=Bruce Min, age=25]

通过上面的实例,大概对序列化和反序列化进行了介绍,需要注意的是,通过反序列获得对象不是原来的对象,即不满足“==”为真的情况,因为,如果我们是序列化一个单例实例的话,就会违反了单例模式的规定(虽然隐藏了单例的构造函数,但是JVM可以通过特殊手段,如反射来构造出对象)。实例如下:

单例类:

package com.minlz.serialize;import java.io.Serializable;/** *  @author minliangzhi *  @date 2016年8月30日 */public class Singleton implements Serializable {    private static final long serialVersionUID = 1L;    private static Singleton singleton;    private Singleton() {            }        public synchronized static Singleton getInstance() {        if(null == singleton) {            singleton = new Singleton();        }                return singleton;    }}

序列化和反序列化的代码:

    public static void main(String[] args) throws Exception {        Singleton s1 = Singleton.getInstance();        System.out.println(s1);        /** 序列化 */        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();        ObjectOutputStream objOut = new ObjectOutputStream(byteOut);        objOut.writeObject(s1);                /** 反序列化 */        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());        ObjectInputStream objIn = new ObjectInputStream(byteIn);        Singleton s2 = (Singleton)objIn.readObject();        System.out.println(s2);  }

测试结果:

com.minlz.serialize.Singleton@47abfd68com.minlz.serialize.Singleton@514ae5cd

可以容易看出来,两个对象不是同一个对象,解决方法是什么呢?

我们在Singleton类中增加一个方法:

    public Object readResolve(){        return getInstance();    }

发现测试结果是:

com.minlz.serialize.Singleton@8523ca2com.minlz.serialize.Singleton@8523ca2

两个对象一致了,所以当面临单例模式实例化的时候,需要实现public Object readResolve()方法,并返回单例实例,就能实现序列化和反序列化对象一致。

 

Java序列化、反序列化和单例模式