JAVA 序列化和反序列化

JAVA 序列化和反序列化

1. 实现方式及侧重点

​ JAVA提供了两种接口实现方式,先看下相关接口结构图
在这里插入图片描述

其中,

1)实现Serializable接口的java类,默认会为内部所有字段(除去transient修饰属性和静态属性)实现序列化和反序列化处理逻辑,适用于序列化属性较多的情形,对于少量的需排除属性,可以使用transient修饰。

2)实现Externalizable接口的java类,需要显式实现writeExternal和readExternal方法,且序列化和反序列化的属性顺序需要一致,适用于序列化属性较少的情形。

2. Serializable方式实证

public class SerializableTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student student = new Student("JEENO", "123456", "Software Engineering");
        student.setBirth(LocalDateTime.now());
        student.setMale(true);

        String filePath = "D:/serializable-demo.text";

        // 序列化 - Serializable方式
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath));
        out.writeObject(student);

        // 这里为了验证serializable序列化不包括静态变量。可以看到反序列后language变成了"Go"
        Student.language = "Go";

        // 反序列化 - Serializable
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));
        Student stu1 = (Student) in.readObject();
        System.out.println(stu1);
    }

    static class Student implements Serializable {
        private String name;
        /**
         * transient 修饰变量,可以避开Serializable序列化
         */
        private transient String password;
        private String major;
        private LocalDateTime birth;
        private Boolean male;
        /**
         * 静态变量,不参与序列化
         */
        public static String language = "JAVA";

        public Student(){}
        public Student(String name, String password, String major) {
            this.name = name;
            this.password = password;
            this.major = major;
        }
		
		// 此处省略字段的getter、setter方法
		...

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", password='" + password + '\'' +
                    ", major='" + major + '\'' +
                    ", birth=" + birth +
                    ", male=" + male +
                    ", language=" + language +
                    '}';
        }
    }

}

运行结果:

Student{name=‘JEENO’, password=‘null’, major=‘Software Engineering’, birth=2021-06-18T00:19:26.759, male=true, language=Go}

容易看到:

  1. transient 修饰字段不参与。student对象中password属性没有参与序列化,在反序列后对象stu1可看到其属性值为null。
  2. 静态属性不参与。 初始状态language为“JAVA”,将student序列化后、反序列化前,将language更新成了“Go”,反序列后对象stu1其属性值也为“Go”。说明在更新值后,即便经过反序列,其值仍未改变,仍旧是读取JVM中Student类的静态属性。

3. Externalizable方式实证

public class ExternalizableTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student student = new Student("JEENO", "123456", "Software Engineering");
        student.setBirth(LocalDateTime.now());
        student.setMale(true);

        String filePath = "D:/externalizable-demo.text";

        // 序列化 - Externalizable方式
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath));
        out.writeObject(student);

        // 更新静态属性值
        Student.language = "Go";

        // 反序列化 - Externalizable
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));
        Student stu1 = (Student) in.readObject();
        System.out.println(stu1);
        System.out.println("current Student's language : " + Student.language);
    }

    static class Student implements Externalizable {
        private String name;
        private transient String password;
        private String major;
        private LocalDateTime birth;
        private Boolean male;
        public static String language = "JAVA";

        public Student() {
        }

        public Student(String name, String password, String major) {
            this.name = name;
            this.password = password;
            this.major = major;
        }

        // 此处省略字段的getter、setter方法
		...

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", password='" + password + '\'' +
                    ", major='" + major + '\'' +
                    ", birth=" + birth +
                    ", male=" + male +
                    ", language=" + language +
                    '}';
        }

        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeObject(this.name);
            out.writeObject(this.password);
            out.writeObject(this.major);
            out.writeObject(this.birth);
            out.writeObject(this.male);
            out.writeObject(language);
        }

        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            this.name = (String) in.readObject();
            this.password = (String) in.readObject();
            this.major = (String) in.readObject();
            this.birth = (LocalDateTime) in.readObject();
            this.male = (Boolean) in.readObject();
            language = (String) in.readObject();
        }
    }
}

运行结果:

Student{name=‘JEENO’, password=‘123456’, major=‘Software Engineering’, birth=2021-06-18T00:24:20.895, male=true, language=JAVA}
current Student’s language : JAVA

可以得到结论:

  1. 区别于Serializable接口实现,Externalizable实现需要实现两个方法,分别为writeExternal和readExternal。并且要显式地指定(序列化/反序列化)顺序;
  2. 不受transient关键字限制。Student中的password属性由于被显式指定,因此也参与了 序列化、反序列化 过程;
  3. 静态属性也可参与。序列化过程中包含了language="JAVA"的数据,因此即便在反序列前已经设置为“Go”,但反序列过程中会将“JAVA”重新写会到类Student中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值