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

本博客主要转自如下链接

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();

    }
}



  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值