序列化和反序列化

目录

1. 定义

2. 使用场景

3. 实现方式

3.1 Serializable接口

3.2 Externalizable接口

4. 参考资料

1. 定义

序列化:把对象转换为字节序列。
反序列化:把字节序列恢复为对象(反序列化得到的是新对象,其属性与序列化时的对象相同)。

2. 使用场景

数据存储:将内存中对象序列化到文件,达到持久化目的。
网络传输:将内存中对象序列化为字节序列,然后在网络中传输。

3. 实现方式

实现Serializable接口或Externalizable接口的类,其对象才可以序列化和反序列化。

3.1 Serializable接口

// 序列化接口,此接口无抽象方法
public interface Serializable {
    
}
class Achievement implements Serializable {
    // 序列化版本号,反序列化时当前类的序列化版本号需要和序列化文件中序列化版本号相同,才能反序列化成功
    private static final long serialVersionUID = 1L;

    private int chinese;
    private int math;

    public Achievement(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    @Override
    public String toString() {
        return "Achievement{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
class Student implements Serializable {
    // 序列化版本号,反序列化时当前类的序列化版本号需要和序列化文件中序列化版本号相同,才能反序列化成功
    private static final long serialVersionUID = 1L;

    private String name;

    private int age;

    // transient或static修饰的属性不参与序列化
    private transient String sex;

    // 当前类若要支持序列化,其内部引用类型属性也需要实现序列化接口
    private Achievement achievement;

    public Student(String name, int age, Achievement achievement) {
        this.name = name;
        this.age = age;
        this.achievement = achievement;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getSex() {
        return sex;
    }

    public Achievement getAchievement() {
        return achievement;
    }

    public void setAchievement(Achievement achievement) {
        this.achievement = achievement;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", achievement=" + achievement +
                '}';
    }
}
public class SerializableTest {

    public static void main(String[] args) {
        serializeStudent();
        deserializeStudent();
    }

    // 序列化
    public static void serializeStudent() {
        Student student = new Student("AAA", 18, new Achievement(90, 90));
        student.setSex("男");
        System.out.println(student);
        // Student{name='AAA', age=18, sex='男', achievement=Achievement{chinese=90, math=90}}
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./Student.txt"));
            oos.writeObject(student);
            oos.close();
        } catch (Exception e) {
            System.out.println("serializeStudent, occur err " + e);
        }
    }

    // 反序列化
    public static void deserializeStudent() {
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./Student.txt"));
            Student newStudent = (Student) ois.readObject();
            System.out.println(newStudent);
            // 因为sex被transient修饰,不参与序列化,所以反序列化时无法获取sex值,反序列化生产的对象中sex就使用默认值
            // Student{name='AAA', age=18, sex='null', achievement=Achievement{chinese=90, math=90}}
            ois.close();
        } catch (Exception e) {
            System.out.println("deserializeStudent, occur err " + e);
        }
    }
}

Serializable接口序列化特点:

1. 执行默认序列化:transient或static修饰的属性不参与序列化,其余属性均参与序列化。

2. 开发者应该显示指定序列化版本号serialVersionUID,反序列化时当前类的serialVersionUID需要和序列化文件中serialVersionUID相同,才能反序列化成功。如果不指定serialVersionUID,那么由编译器通过某种算法根据对象信息(类型、属性等)生成默认serialVersionUID,由于不同编译器的差异,可能导致反序列化时当前类的serialVersionUID和序列化文件中serialVersionUID不一致,反序列化失败。或者,序列化后,当前类新增了属性,那么生成了新的默认serialVersionUID,和序列化文件中的serialVersionUID不一致,导致序列化失败;但是如果序列化前当前类显示指定了serialVersionUID,即使序列化后当前类新增了属性,反序列化也可以兼容成功。

3. 当前类若要支持序列化,其内部引用类型属性也需要实现序列化接口。

4. 反序列化读取数据需要和序列化时写入数据的顺序保持一致.

5. 反序列化不会调用类的构造函数,而是由JVM生成对象。

6. Serializable实现类中新增writeObject方法和readObject方法,可以实现自定义序列化。代码如下所示:

class Student implements Serializable {
    // 序列化版本号,反序列化时当前类的序列化版本号需要和序列化文件中序列化版本号相同,才能反序列化成功
    private static final long serialVersionUID = 1L;

    private String name;

    private int age;

    // transient或static修饰的属性不参与序列化
    private transient String sex;

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

    // writeObject方法既不是Object类的,也不是Serializable接口的
    // writeObject方法必须被private修饰,由ObjectOutputStream执行writeObject方法进行序列化时,通过反射调用
    // 反序列化读取数据需要和序列化时写入数据的顺序保持一致
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.writeObject(this.name);
        oos.writeInt(this.age);
        // 也可以使用oos.defaultWriteObject();代替上面两行代码,defaultWriteObject方法会序列化非transient或非static修饰的属性
        oos.writeObject(this.sex);
        // 即使sex被transient修饰,但是自定义序列化时写入了sex,sex也会参与序列化
    }

    // readObject方法既不是Object类的,也不是Serializable接口的
    // readObject方法必须被private修饰,由ObjectInputStream执行readObject方法进行反序列化时,通过反射调用
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        this.name = (String) ois.readObject();
        this.age = ois.readInt();
        // 也可以使用ois.defaultReadObject();代替上面两行代码,defaultReadObject方法会反序列化非transient或非static修饰的属性
        this.sex = (String) ois.readObject();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getSex() {
        return sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
public class SerializableTest {

    public static void main(String[] args) {
        serializeStudent();
        // deserializeStudent();
    }

    // 序列化
    public static void serializeStudent() {
        Student student = new Student("AAA", 18);
        student.setSex("男");
        System.out.println(student);
        // Student{name='AAA', age=18, sex='男'}
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./Student.txt"));
            oos.writeObject(student);
            oos.close();
        } catch (Exception e) {
            System.out.println("serializeStudent, occur err " + e);
        }
    }

    // 反序列化
    public static void deserializeStudent() {
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./Student.txt"));
            Student newStudent = (Student) ois.readObject();
            System.out.println(newStudent);
            // Student{name='AAA', age=18, sex='男'}
            ois.close();
        } catch (Exception e) {
            System.out.println("deserializeStudent, occur err " + e);
        }
    }

}

3.2 Externalizable接口

// 序列化接口,开发者可自定义哪些属性参与序列化和反序列化
public interface Externalizable extends java.io.Serializable {
    // 自定义序列化过程
    void writeExternal(ObjectOutput out) throws IOException;
    
    // 自定义反序列化过程
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
class Student implements Externalizable {
    // 序列化版本号,反序列化时当前类的序列化版本号需要和序列化文件中序列化版本号相同,才能反序列化成功
    private static final long serialVersionUID = 1L;

    private String name;

    private int age;

    // transient或static修饰的属性不参与序列化
    private transient String sex;

    // 必须提供public类型的无参构造函数
    public Student() {

    }

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

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(this.name);
        out.writeInt(this.age);
        out.writeObject(this.sex); 
        // 即使sex被transient修饰,但是自定义序列化时写入了sex,sex也会参与序列化
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.name = (String) in.readObject();
        this.age = in.readInt();
        this.sex = (String) in.readObject();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getSex() {
        return sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

}
public class SerializableTest {

    public static void main(String[] args) {
        Student student = new Student("AAA", 18);
        student.setSex("男");
        System.out.println(student); // Student{name='AAA', age=18, sex='男'}

        serializeStudent(student);

        Student newStudent = deserializeStudent();
        System.out.println(newStudent); // Student{name='AAA', age=18, sex='男'}

        System.out.println(student == newStudent); // false
    }

    // 序列化
    public static void serializeStudent(Student student) {
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./Student.txt"));
            oos.writeObject(student);
            oos.close();
        } catch (Exception e) {
            System.out.println("serializeStudent, occur err " + e);
        }
    }

    // 反序列化
    public static Student deserializeStudent() {
        Student newStudent = null;
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./Student.txt"));
            newStudent = (Student) ois.readObject();
            ois.close();
        } catch (Exception e) {
            System.out.println("deserializeStudent, occur err " + e);
        }
        return newStudent;
    }

}

Externalizable接口序列化特点:

1. 必须实现writeExternal方法(自定义序列化过程)和readExternal方法(自定义反序列化过程)。

2. 必须提供public类型的无参构造函数。

4. 参考资料

Java序列化与反序列化

Java序列化的几种方式

什么是Java的序列化?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值