Java序列化关键字-Serializable解析

  Serializable意指序列化,即把Java对象转化为二进制字节序列,同样,反序列化则是把之前对象生成的二进制字节序列还原成Java对象。它的存在意义是什么?现在的网络架构和硬盘只能识别二进制字节,假如要直接把Java对象在网络上传输,或者直接存入硬盘里,人家是根本不识别的。所以如果要把对象在网络上进行传输或者写入文件里,序列化是前提。另外需要了解的是:Serializable这个接口并没有定义要求你来实现的方法,它属于一钟标识接口,加上它就只是为了告诉别人:哦,你是可序列化的~

  Java中采用ObjectInputStream来反序列化文件里的字节还原成对象,采用ObjectOutputStream来将对象变成字节写入文件中。

  下面定义一个对象:User

public class User implements Serializable{
    private int id;
    private String name;
    public User() {
        // TODO Auto-generated constructor stub
    }
    
    public User(int id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + "]";
    }
    
}

  下面开始写入文件里:

public static void main(String[] args) {
        // TODO Auto-generated method stub
        User user=new User(1111,"hello");
        ObjectOutputStream oos=null;
        try {
            oos=new ObjectOutputStream(new FileOutputStream("E://user.txt"));
            oos.writeObject(user);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            try {
                oos.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

  假如未实现Serializable接口,会产生如下异常:

  写入文件后,看看长什么样子:

  嗯,很明显,根本看不懂。

  接下来用ObjectInputStream读取:

  

  简单的写入读取就是这些,接下来重点来了:实现Serializable接口,会有感叹号警示,如果想要去除它,需要添加一个默认的版本ID,或者添加一个根据类本身信息自动生成的版本ID:

private static final long serialVersionUID = -1928208351401916910L;//自动生成

  那么它的作用是什么呢?serialVersionUID用于版本控制,用于保证同一个对象可以在反序列化过程中被载入。怎么理解?下面用代码来说话:

  我在不生成serialVersionUID的情况下,在原来的user类里加一个字段age,继续执行刚才的反序列化,会有以下异常:

 

  这是在告诉你,最初的的时候版本号是-192.....,而你增加了一个字段后版本号改变成另一个了,再次反序列化,流根本不认识你这个‘新’的类(其实只是原来的类增加了字段而已),所以抛出异常。好比你换个发型带个眼镜,你还是你,公司前台人员却觉得不是你本人把你拒之门外(当然没这么蠢的前台人员。。)

  而如果你一开始就生成了这个序列号,无论后面怎样添加新的字段都是可以继续反序列化的。好比你刚一开始就录入了自己的指纹,即便怎么化妆打扮你依然是你,公司的门随意出入。

  之前讲过的单例模式,除了枚举那种写法外,其余都是无法避免反序列化的,下面把User改成饿汉单例模式

public class User implements Serializable{
    
    private static final long serialVersionUID = -1928208351401916910L;
    
    private int id;
    private String name;
    private int age;
    private User() {
        // TODO Auto-generated constructor stub
    }
    
    private static User user=new User();
    public static User getUser(){
        return user;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + "]";
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
}

  执行反序列进行比较:

  此时已经不再是同一个对象了。

  最后还有几点需要注意:1,由于静态变量是类级别的,不是对象级别的,当你序列化一个对象的时候,是不能序列化静态变量的。

             2,假如你的对象有个字段引用了其他对象,而这个对象对应的类并没有实现Serializable接口,那么这个字段将无法被序列化(会报错)。如果你想忽略它,就在字段前加上transient(短暂的)。

               3,继承这方面也有一定说法,我就不总结了。详情请参考:http://www.oschina.net/translate/serialization-in-java

  OK,感觉还不错,还是手生。

posted @ 2016-12-14 21:54  平凡之路-不甘平凡  阅读(128)  评论(0)    收藏  举报