序列化 ID 有什么用?

如果不指定 serialVersionUID ,当你添加或者修改类中的任何字段时,已序列化类将无法恢复。因为新类和旧序列化对象生成的serialVersionUID 不同,序列化的过程将依赖正确的序列化对象恢复状态的。否则会报错 java.io.InvalidClassException 。

初始代码:  指定了 serialVersionUID 的情况下

serialVersionUID import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable {

    /**
     * 不指定 serialVersionUID的后果是:当你添加或修改类中的任何字段时,已序列化类将无法恢复,因为新类和旧序列化对象生成的
     * serialVersionUID 将有所不同。Java 序列化的过程是依赖于正确的序列化对象恢复状态的,并在序列化对象序列版本不匹配的情况下引发
     * java.io.InvalidClassException 无效类异常。
     */
    private static final long serialVersionUID = 1L;// 7552393449144317442L;

    private String name;

    private String password;

    private int age;

    public static int grade;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}';
    }
}

public class WriteObject {

    public static void main(String[] args) {
        try {

            // 序列化步骤,新建一个 ObjectOutputStream 对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
            Person person = new Person("xiaoming", 19); // 序列化 写入对象
            person.grade = 2;
            oos.writeObject(person);


            // 反序列化 新建一个ObjectInputStream
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
            // 反序列化 读取
            Person per = (Person) ois.readObject();
            System.out.println(per);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果

Person{name='xiaoming', age=19, grade=2}

 

serialVersionUID 不变,加个字段,看是否能够能够读出

package JavaBase.serializable;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable {

    /**
     * 不指定 serialVersionUID的后果是:当你添加或修改类中的任何字段时,已序列化类将无法恢复,因为新类和旧序列化对象生成的
     * serialVersionUID 将有所不同。Java 序列化的过程是依赖于正确的序列化对象恢复状态的,并在序列化对象序列版本不匹配的情况下引发
     * java.io.InvalidClassException 无效类异常。
     */
    private static final long serialVersionUID = 1L;// 7552393449144317442L;

    private String name;

    private String password;

    private int age;

    public static int grade;

    public int i;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}';
    }
}

public class WriteObject {

    public static void main(String[] args) {
        try {

            // 序列化步骤,新建一个 ObjectOutputStream 对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
            Person person = new Person("xiaoming", 19); // 序列化 写入对象
            person.grade = 2;
            oos.writeObject(person);


            // 反序列化 新建一个ObjectInputStream
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
            // 反序列化 读取
            Person per = (Person) ois.readObject();
            System.out.println(per);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

执行结果:

Person{name='xiaoming', age=19, grade=2}

 

变量不变,改变 serialVersionUID = 2L 看是否能解析出, 答案是会报错

package JavaBase.serializable;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable {

    /**
     * 不指定 serialVersionUID的后果是:当你添加或修改类中的任何字段时,已序列化类将无法恢复,因为新类和旧序列化对象生成的
     * serialVersionUID 将有所不同。Java 序列化的过程是依赖于正确的序列化对象恢复状态的,并在序列化对象序列版本不匹配的情况下引发
     * java.io.InvalidClassException 无效类异常。
     */
    private static final long serialVersionUID = 2L;// 7552393449144317442L;

    private String name;

    private String password;

    private int age;

    public static int grade;

    public int i;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + ", grade=" + grade + '}';
    }
}

public class WriteObject {

    public static void main(String[] args) {
        try {

          /*  // 序列化步骤,新建一个 ObjectOutputStream 对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
            Person person = new Person("xiaoming", 19); // 序列化 写入对象
            person.grade = 2;
            oos.writeObject(person);
            */

            // 反序列化 新建一个ObjectInputStream
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
            // 反序列化 读取
            Person per = (Person) ois.readObject();
            System.out.println(per);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

执行结果:

 

java.io.InvalidClassException: JavaBase.serializable.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
    at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
    at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
    at java.io.ObjectInputStream.readClassDesc(Unknown Source)
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
    at java.io.ObjectInputStream.readObject0(Unknown Source)
    at java.io.ObjectInputStream.readObject(Unknown Source)
    at JavaBase.serializable.WriteObject.main(WriteObject.java:56)

 

 

 

静态变量的序列化

序列换保存的对象的状态,静态变量属于类的状态,序列化并不保存静态变量。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * 类说明 最后的输出是 10,对于无法理解的读者认为,打印的 staticVar 是从读取的对象里获得的,应该是保存时的状态才对。之所以打印 10
 * 的原因在于序列化时,并不保存静态变量,这其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
 * 
 */
public class TestStaticSerializable implements Serializable {

    private static final long serialVersionUID = 1L;

    public static int staticVar = 5;

    public static void main(String[] args) {
        try {
            // 初始时staticVar为5
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("result.obj"));
            // 序列化后修改为10
            TestStaticSerializable.staticVar = 10;
            out.writeObject(new TestStaticSerializable());
            out.close();

            ObjectInputStream oin = new ObjectInputStream(new FileInputStream("result.obj"));
            TestStaticSerializable t = (TestStaticSerializable) oin.readObject();
            oin.close();

            // 再读取,通过t.staticVar打印新的值
            System.out.println(t.staticVar);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

 

输出的结果是 10 而不是 5 ,序列化保存的是对象的状态,并不保存(静态变量)类状态

父类序列化和 Transient 关键字

 

Transient 关键字的作用是控制变量的序列化,在关键字前加上该关键字,可以阻止变量被序列化到文件中,反序列化之后,trasnient 变量的值被设为初始值,int 类型的是 0 ,对象型的是 null。

一个子类实现了Serializable 接口,父类都没有实现 Serializable 接口,序列化子类对象,然后反序列化后输出父类定义的某个变量的值,改变量值与序列化时的数值是不同的。

Java 序列化问题_Java

父类:

public class Parent {

    private String attr1;

    private String attr2;

    private String attr3;

    public Parent() {

    }

    public Parent(String attr1, String attr2, String attr3) {
        this.attr1 = attr1;
        this.attr2 = attr2;
        this.attr3 = attr3;
    }

    public String getAttr1() {
        return attr1;
    }

    public void setAttr1(String attr1) {
        this.attr1 = attr1;
    }

    public String getAttr2() {
        return attr2;
    }

    public void setAttr2(String attr2) {
        this.attr2 = attr2;
    }

    public String getAttr3() {
        return attr3;
    }

    public void setAttr3(String attr3) {
        this.attr3 = attr3;
    }

}

 

子类:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

public class Child extends Parent implements Serializable {

    private String attr4;

    private transient String attr5;

    public String getAttr4() {
        return attr4;
    }

    public void setAttr4(String attr4) {
        this.attr4 = attr4;
    }

    public String getAttr5() {
        return attr5;
    }

    public void setAttr5(String attr5) {
        this.attr5 = attr5;
    }

    public Child(String attr1, String attr2, String attr3, String attr4, String attr5) {
        super(attr1, attr2, attr3);
        this.attr4 = attr4;
        this.attr5 = attr5;
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        File file = new File("D:" + File.separator + "s.txt");
        OutputStream os = new FileOutputStream(file);
        ObjectOutputStream oos = new ObjectOutputStream(os);
        oos.writeObject(new Child("str1", "str2", "str3", "str4", "str5"));
        oos.close();

        InputStream is = new FileInputStream(file);
        ObjectInputStream ois = new ObjectInputStream(is);
        Child so = (Child) ois.readObject();
        System.out.println("str1 = " + so.getAttr1());
        System.out.println("str2 = " + so.getAttr2());
        System.out.println("str3 = " + so.getAttr3());
        System.out.println("str4 = " + so.getAttr4());
        System.out.println("str5 = " + so.getAttr5());
        ois.close();
    }
}

 

执行结果:

str1 = null
str2 = null
str3 = null
str4 = str4
str5 = null