Java序列化之serialVersionUID

一、问题
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();
    }
}
序列化后文件:
seralizable.txt

以上类增加一个属性重新编译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 +
             '}';
    }
}
此处序列化代码见上面。
序列化后文件:
seralizable.txt

以上类增加一个属性重新编译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、当一个对象的实例变量引用其它对象,序列化该对象时也把引用对象进行序列化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值