您的位置:首页 > 产品设计 > UI/UE

Java序列化之serialVersionUID

2016-11-17 10:12 423 查看
一、问题

1、声明serialVersionUID的作用是什么?

2、JVM如何通过serialVersionUID属性来确定是否可反序列化,怎么起作用的?

3、继承java.io.Serializable接口后不声明serialVersionUID属性Eclipse会发出警告,怎么解决?

4、继承java.io.Serializable接口后不声明serialVersionUID属性,Java是如何处理序列化与反序列化的?

5、不声明serialVersionUID属性,对象序列化后,对象属性声明发生变化,这时可以反序列化回来么?

6、声明serialVersionUID属性,对象序列化后,对象属性声明发生变化,这时可以反序列化回来么?

7、如果没有定义serialVersionUID,缺省的serialVersionUID值是怎么确定的?什么时候确定的(编译还是运行时)?缺省的serialVersionUID什么时候会发生变化?我们写的类是否应该推荐定义serialVersionUID?

8、显示定义serialVersionUID的两种用途
二、解决问题

1、声明serialVersionUID的作用是什么?

Java在类序列化与反序列化过程中,通过serialVersionUID来确定是否可进行反序列化。

2、JVM如何通过serialVersionUID属性来确定是否可反序列化,怎么起作用的?

类在序列化时,Java根据serialVersionUID标识将对象转成字节序列存储至硬件硬盘或传输在网络上;在进行反序列化时,JVM把字节流中的serialVersionUID与本地类的serialVersionUID进行比较,如果相同即可进行反序列号,否则会出现序列化版本不一致异常(InvalidCastException)。

3、继承java.io.Serializable接口后不声明serialVersionUID属性Eclipse会发出警告,怎么解决?

当类实现 java.io.Serializable
时,说明这个类可被序列化,这时eclipse会发出警告(The serializable class Test does not declare a static final serialVersionUID field of type long),要解决这个警告必须声明属性serialVersionUID并且给属性赋值。serialVersionUID的生成方式有两种:一是缺省的1L(private
static final long serialVersionUID = 1L;);另一个是根据类名、接口名、成员方法及属性来生成一个64位的哈希字段,如:(private static final long serialVersionUID = -344227642091683711L;);

移动鼠标到类名上或单击类名:

Add default serial version ID

Add generated serial version ID

4、继承java.io.Serializable接口后不声明serialVersionUID属性,Java是如何处理序列化与反序列化的?

若没有显示的去声明这个属性,Java序列化机制会根据编译的class的类名、方法名等因素自动生成一个serialVersionUID用于反序列化比较,这种情况下class没有发生变化的时候(类增加注释或空格)无论编译任何次数,此属性的值是不会发生变化的。

5、不声明serialVersionUID属性,对象序列化后,对象属性声明发生变化,这时可以反序列化回来么?

序列化前Java类 SmallDog.java(未显示声明serialVersionUID属性)
public class SmallDog implements Serializable{

    private String name;

    private Double weight;

    private int age;

    public SmallDog() {}

    public SmallDog(String name, Double weight, int age) {

        this.name = name;

        this.weight = weight;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Double getWeight() {

        return weight;

    }

    public void setWeight(Double weight) {

        this.weight = weight;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    @Override

    public String toString() {

        return "SmallDog{" +

             "name='" + name + '\'' +

             ", weight=" + weight +

             ", age=" + age +

             '}';

    }

}
序列化类代码片段
public void testSeralizableSmallDog(){

    SmallDog smallDog = new SmallDog("小白", 4.0, 1);

    try {

        FileOutputStream fos = new FileOutputStream("seralizable.txt");

        ObjectOutputStream oos = new ObjectOutputStream(fos);

        oos.writeObject(smallDog);

        oos.flush();

        oos.close();

    } catch (IOException e) {

        e.printStackTrace();

    }

}
序列化后文件:

以上类增加一个属性重新编译SmallDog.java
public class SmallDog implements Serializable{

    private String name;

    private Double weight;

    private int age;

    private String sex;

    public SmallDog() { }

    public SmallDog(String name, Double weight, int age) {

        this.name = name;

        this.weight = weight;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Double getWeight() {

        return weight;

    }

    public void setWeight(Double weight) {

        this.weight = weight;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getSex() {

        return sex;

    }

    public void setSex(String sex) {

        this.sex = sex;

    }

    @Override

    public String toString() {

        return "SmallDog{" +

            "name='" + name + '\'' +

            ", weight=" + weight +

            ", age=" + age +

            '}';

    }

}
反序列化代码片段
public void deserializeSmallDog(){

    try {

        SmallDog smallDog;

        FileInputStream fis = new FileInputStream(new File("seralizable.txt"));

        ObjectInputStream ois = new ObjectInputStream(fis);

        smallDog = (SmallDog)ois.readObject();

        System.out.println(smallDog.toString());

        ois.close();

    } catch (IOException e) {

        e.printStackTrace();

    } catch (ClassNotFoundException e) {

        e.printStackTrace();

    }

}
输出结果
java.io.InvalidClassException: com.smalldog.implseralizable.SmallDog; local class incompatible: stream classdesc serialVersionUID = -3046572760386773397, local class serialVersionUID = 359715241838742995

at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)

at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)

at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)

at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)

at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)

at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)

at com.smalldog.SerializableTestCase.deserializeSmallDog(SerializableTestCase.java:47)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:497)

at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)

at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)

at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)

at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)

at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)

at org.junit.runners.ParentRunner.run(ParentRunner.java:236)

at org.junit.runner.JUnitCore.run(JUnitCore.java:157)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)

at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)

at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)

at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
6、声明serialVersionUID属性,对象序列化后,对象属性声明发生变化,这时可以反序列化回来么?

序列化前Java类 SmallDog.java(显示声明serialVersionUID属性)
public class SmallDog implements Serializable{

    private static final long serialVersionUID = 1L;

    private String name;

    private Double weight;

    private int age;

    public SmallDog() {}

    public SmallDog(String name, Double weight, int age) {

        this.name = name;

        this.weight = weight;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Double getWeight() {

        return weight;

    }

    public void setWeight(Double weight) {

        this.weight = weight;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    @Override

    public String toString() {

        return "SmallDog{" +

             "name='" + name + '\'' +

             ", weight=" + weight +

             ", age=" + age +

             '}';

    }

}
此处序列化代码见上面。

序列化后文件:

以上类增加一个属性重新编译SmallDog.java
public class SmallDog implements Serializable{

    private static final long serialVersionUID = 1L;  

    private String name;

    private Double weight;

    private int age;

    private String sex;

    public SmallDog() { }

    public SmallDog(String name, Double weight, int age) {

        this.name = name;

        this.weight = weight;

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public Double getWeight() {

        return weight;

    }

    public void setWeight(Double weight) {

        this.weight = weight;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getSex() {

        return sex;

    }

    public void setSex(String sex) {

        this.sex = sex;

    }

    @Override

    public String toString() {

        return "SmallDog{" +

            "name='" + name + '\'' +

            ", weight=" + weight +

            ", age=" + age +

            '}';

    }

}
输出结果
SmallDog{name='小白', weight=4.0, age=1}
7、如果没有定义serialVersionUID,缺省的serialVersionUID值是怎么确定的?什么时候确定的(编译还是运行时)?缺省的serialVersionUID什么时候会发生变化?我们写的类是否应该推荐定义serialVersionUID?

在序列化运行时将基于该类的各个方面计算该类的默认serialVersionUID值。缺省的serialVersionUID完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同;在类被改变(不包含改变注释或空格回车等)后重新编译后,serialVersionUID会发生变化。强烈建议所有可序列化的类都显示声明serialVersionUID值,因为计算默认的serialVersionUID对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的InvalidClassException
;为保证serialVersionUID值跨不同Java编译器实现的一致性,序列化类必须声明一个明确的serialVersionUID值;还强烈建议使用private修饰符显示声明serialVersionUID,因为这种声明仅应用于直接声明类,作为继承成员没有用处。数组类不能声明一个明确的serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配serialVersionUID值的要求。

8、显示定义serialVersionUID的两种用途

a.在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

b.当你序列化了一个类实例后,希望更改一个字段或添加一个字段,不设置serialVersionUID,所做的任何更改都将导致无法反序列化旧的实例,并在反序列化时抛出一个异常。如果你添加了serialVersionUID,在反序列化旧实例时,新添加或更改的字段值将设为初始化值,字段被删除将不设置。
三、注意事项

1、序列化时,只对对象的状态进行保存,不管对象的方法;

2、当一个父类实现序列化,子类自动实现序列化,不需要显示实现java.io.Serializable接口;

3、当一个对象的实例变量引用其它对象,序列化该对象时也把引用对象进行序列化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: