Java对象的serialVersionUID在序列化和反序列化的用途

来源:互联网 发布:sql msde 编辑:程序博客网 时间:2024/06/05 15:40

本博客主要转自如下链接

http://blog.csdn.net/javazejian/article/details/52665164

这篇文章写的不错,但是有些地方我估计博主没有亲自测试,所以有些地方我亲测后发现其实他说的不对,大家可以先看看他写的,然后再看看我下面说的具体哪里不对了

先说说serialVersionUID的用途:

其实一个类要实现序列化和反序列化,不声明serialVersionUID是可行的,serialVersionUID主要是辅助序列化和反序列化,序列化和反序列化的时候,serialVersionUID必须一致,反序列化才会成功。具体的序列化过程是这样的:序列化操作的时候系统会把当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会去检测文件中的serialVersionUID,判断它是否与当前类的serialVersionUID一致,如果一致就说明序列化类的版本与当前类版本是一样的,可以反序列化成功,否则失败。报出如下UID错误:

Exception in thread "main" java.io.InvalidClassException: com.zejian.test.Client; local class incompatible: stream classdesc serialVersionUID = -2083503801443301445, local class serialVersionUID = -4083503801443301445

原博文中主要错在这句话  “因此强烈建议指定serialVersionUID,这样的话即使微小的变化也不会导致crash的出现,如果不指定的话只要这个文件多一个空格,系统自动生成的UID就会截然不同的,反序列化也就会失败

亲测发现,当不指定serialVersionUID的时候,将对象序列化到文件中后,如果仅仅在对象文件中添加了一些空格或者换行,反序列化仍然会成功,而并不是那篇博客中说的加个空格就会反序列化失败。原因应该是serialVersionUID的生成方法是基于对象的一些属性生成的,而跟这个对象文件中的空格换行等是没有关系的。

我试着在没有指定的serialVersionUID的情况下,序列化后,在对象中添加了一个属性,并对之前生成的序列化后的文件进行反序列化,发现失败了,抛了上述说的异常,这是因为在对象中添加了属性后,由于没有指定serialVersionUID,所以系统会自动根据这个对象的属性等产生一个新的serialVersionUID,这就导致这个serialVersionUID与序列化文件中的serialVersionUID不一致,从而反序列化失败

接着,我为对象加了serialVersionUID属性,在intellij idea中为一个对象加serialVersionUID的方法如下:

Setting->Inspections->Serialization issues->Serializable class without ’serialVersionUID’
选中以上后,在你的class中:光标定位在类名前,按 Alt+Enter 就会提示自动创建 serialVersionUID 了。
在添加了serialVersionUID的对象中,我重新进行了序列化,序列化后,我在对象中添加了一个新的属性,也修改了原有的属性,但是serialVersionUID没有变,所以序列化文件中的serialVersionUID和对象文件中的serialVersionUID一致,反序列化都成功,但是只会反序列化成功匹配上的属性值,没有匹配上的都会为null。如果在序列化后修改了serialVersionUID的值,则反序列化会失败,抛出上述异常。

下面是上述博客中测试的例子,我进行了点修改方便测试:

被序列化对象Client:注意,这个对象中的几个object相关方法是用来自定义序列化和反序列化过程的,我们自定义哪些属性需要序列化,上述测试我把object相关方法都注释掉了。

package com.sogou.study.serialize;/** * Created by denglinjie on 2016/9/29. */import java.io.IOException;import java.io.ObjectStreamException;import java.io.Serializable;public class Client implements Serializable{    /**     * 生成序列号标识     */    private static final long serialVersionUID = -2083503801443301445L;    private int id;    private String 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;    }    /**     * 序列化时,     * 首先系统会先调用writeReplace方法,在这个阶段,     * 可以进行自己操作,将需要进行序列化的对象换成我们指定的对象.     * 一般很少重写该方法     * @return     * @throws ObjectStreamException     */    private Object writeReplace() throws ObjectStreamException {        System.out.println("writeReplace invoked");        return this;    }    /**     *接着系统将调用writeObject方法,     * 来将对象中的属性一个个进行序列化,     * 我们可以在这个方法中控制住哪些属性需要序列化.     * 这里只序列化name属性     * @param out     * @throws IOException     */    private void writeObject(java.io.ObjectOutputStream out) throws IOException {        System.out.println("writeObject invoked");        //这里自定义只序列化了对象的name属性到文件中        out.writeObject(this.name == null ? "zejian" : this.name);    }    /**     * 反序列化时,系统会调用readObject方法,将我们刚刚在writeObject方法序列化好的属性,     * 反序列化回来.然后通过readResolve方法,我们也可以指定系统返回给我们特定的对象     * 可以不是writeReplace序列化时的对象,可以指定其他对象.     * @param in     * @throws IOException     * @throws ClassNotFoundException     */    private void readObject(java.io.ObjectInputStream in) throws IOException,            ClassNotFoundException {        System.out.println("readObject invoked");        //由于序列化的时候自定义只序列化了name属性,所以这里读出来的object就是name的值,可以直接转换成String        this.name = (String) in.readObject();        System.out.println("got name:" + name);    }    /**     * 通过readResolve方法,我们也可以指定系统返回给我们特定的对象     * 可以不是writeReplace序列化时的对象,可以指定其他对象.     * 一般很少重写该方法     * @return     * @throws ObjectStreamException     */    private Object readResolve() throws ObjectStreamException {        System.out.println("readResolve invoked");        return this;    }}

序列化类

package com.sogou.study.serialize;/** * Created by denglinjie on 2016/9/29. */import java.io.FileOutputStream;import java.io.ObjectOutputStream;public class SerializeTest {    public static void main(String[] args) throws Exception {        //把对象序列化到文件        Client client = new Client();        client.setId(11);        client.setName("client");        ObjectOutputStream oo = new ObjectOutputStream                (new FileOutputStream("D:\\test\\file1.txt"));        oo.writeObject(client);        oo.close();    }}

反序列化类:

package com.sogou.study.serialize;/** * Created by denglinjie on 2016/9/29. */import java.io.FileInputStream;import java.io.ObjectInputStream;public class UnSerializeTest {    public static void main(String[] args) throws Exception {        //反序列化到内存        ObjectInputStream oi = new ObjectInputStream                (new FileInputStream("D:\\test\\file1.txt"));        Client c_back = (Client) oi.readObject();        System.out.println(c_back.getName());        System.out.println(c_back.getId());        oi.close();    }}



0 0