前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 序列化问题

Java 序列化问题

作者头像
王小明_HIT
发布2020-03-25 09:33:29
5010
发布2020-03-25 09:33:29
举报
文章被收录于专栏:程序员奇点程序员奇点

序列化 ID 有什么用?

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

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

代码语言:javascript
复制
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();
        }
    }
}

执行结果

代码语言:javascript
复制
Person{name='xiaoming', age=19, grade=2}

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

代码语言:javascript
复制
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();
        }
    }
}

执行结果:

代码语言:javascript
复制
Person{name='xiaoming', age=19, grade=2}

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

代码语言:javascript
复制
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();
        }
    }
}

执行结果:

代码语言:javascript
复制
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)

静态变量的序列化

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

代码语言:javascript
复制
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 接口,序列化子类对象,然后反序列化后输出父类定义的某个变量的值,改变量值与序列化时的数值是不同的。

父类:

代码语言:javascript
复制
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;
    }

}

子类:

代码语言:javascript
复制
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();
    }
}

执行结果:

代码语言:javascript
复制
str1 = null
str2 = null
str3 = null
str4 = str4
str5 = null
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员奇点 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档