关于The serializable class does not declare a static final serialVersionUID field of type long

来源:互联网 发布:淘宝专卖店与专营店 编辑:程序博客网 时间:2024/05/22 04:48

当我们需要在Intent中传类时,我们会想起Serializable和Parcelable。当我们需要将类写入文件时,会想起Serializable。

这里我们讲下Serializable,使用它只需实现Serializable接口即可,但是在此时往往会提示”The serializable class does not declare a static final serialVersionUID field of type long”。这篇文章讲的主要是如何解决以及为什么会出现。

知其缘由

上面我们所说的两种用途涉及到类的序列化与反序列化,经常用来做版本控制。在这个过程中,JVM会比较字节流中的serialVersionUID 与本地类的serialVersionUID是否一致。如果不一致,会报InvalidClassException的异常。

定义形式如下:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

我们可以书写下下面的测试样例。

实体类:

package serialVersionUID;import java.io.Serializable;public class User implements Serializable {    private static final long serialVersionUID = -8003021310975257817L;    public String name;    public String address;    public int userId;    public int getUserId() {        return userId;    }    public void setUserId(int userId) {        this.userId = userId;    }    public void setName(String name) {        this.name = name;    }    public void setAddress(String address) {        this.address = address;    }    public String getName() {        return this.name;    }    public String getAddress() {        return this.address;    }    @Override    public String toString() {        return name + ":" + address + ":" + userId;    }}

写入文件:

package serialVersionUID;import java.io.FileOutputStream;import java.io.ObjectOutputStream;public class WriteObject {    public static void main(String[] argc) {        User user = new User();        user.setName("jerry");        user.setAddress("dont want to tell u");        try {            FileOutputStream fos = new FileOutputStream("D:\\Users\\LINHAIYANG\\Desktop\\java_test\\serialVersionUID\\user.jer");            ObjectOutputStream oos = new ObjectOutputStream(fos);            oos.writeObject(user);            oos.close();            System.out.println("done");        } catch (Exception e) {            e.printStackTrace();        }    }}

读取实体:

package serialVersionUID;import java.io.FileInputStream;import java.io.ObjectInputStream;public class ReadObject {    public static void main(String[] argc) {        User user = null;        try {            FileInputStream fis = new FileInputStream("D:\\Users\\LINHAIYANG\\Desktop\\java_test\\serialVersionUID\\user.jer");            ObjectInputStream ois = new ObjectInputStream(fis);            user = (User) ois.readObject();            ois.close();            System.out.println(user);        } catch (Exception e) {            e.printStackTrace();        }    }}

根据上面的例子,当我们将类写入文件后,修改实体类的serialVersionUID时,再去读取实体,就会出现UID不一致的情况,报InvalidClassException的异常。

如果不定义

上面是因为我们手动修改了serialVersionUID导致的异常,如果我们使用Serializable不定义这个UID,会怎么样?

如果我们不定义UID,JVM会自动的生成UID,而生成方式与许多东西有关,例如JVM版本、文件存储方式(例如Windows与Ubuntu)有关、类属性方法等有关。

由于不方便测试JVM版本、文件存储方式对UID的影响,这里只测试类属性方法对UID的影响。使用如下代码获取类的UID:

ObjectStreamClass osc = ObjectStreamClass.lookup(User.class);System.out.print(osc.getSerialVersionUID());        

根据这段代码在我的电脑下测试如下:
初始UID: -4269709306655343749
增加空格UID: -4269709306655343749
增加属性UID: -2183236014181697252

public int a;

增加方法UID: 7125364192856802935

public void a() {}

这里我们可以看到,增加属性和方法会对UID造成影响。因此,如果我们不定义UID的话,随着代码迭代,实体难免会修改属性或者方法。这样的话,以前写入文件的读取时就会产生异常。
同理的,JVM版本和文件存储方式也会对UID造成影响。

因此,建议在每个实体类都定义一个UID,以免产生问题。

如何定义

定义serialVersionUID有两种方式:

一是默认的1L,比如:

private static final long serialVersionUID = 1L; 

二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,这个编译器可以帮我们生成,比如:

private static final long serialVersionUID = xxxxL;

这两种方法根据文档来说没什么区别。当新版本需要兼容旧版本时,UID应保持不变,否则应该修改UID。

参考

  • serialVersionUID计算方法
    http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100
  • 好的介绍文章
    http://www.mkyong.com/java-best-practices/understand-the-serialversionuid/
  • 官方文档
    http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html
0 0
原创粉丝点击