Android基础系列(4)Java序列化与反序列化

424 阅读3分钟

前言

Serializable是对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。
我们将从以下几个问题来深入了解Serializable。

  1. 什么是序列化与反序列化?
  2. 为什么要进行序列化?
  3. Serializable在序列化过程中起到了什么作用?

1 序列化与反序列化

  • Java序列化是指把Java对象转换为字节序列的过程。
  • Java反序列化是指把字节序列恢复为Java对象的过程

2 为什么要进行序列化

  因为Java的对象本质上就是class字节码,如果你把对象传给服务器这些,它们是不能识别你传的是一个Java对象。所以这个时候,我们就需要一个约定好的格式,让大家都能识别这是一个对象。
  这也就是为什么要进行对象序列化。
  把对象序列化为一个有序字节序列后,我们不管是存储到文件,还是传输给服务器等,都很方便,大家也都能识别这是一个Java对象。

3 Serializable作用

Serializable接口就是让JVM知道,这个对象要进行序列化。

public interface Serializable {
}

所以这个接口并没有什么方法需要实现。下面我们实际来用一下序列化。

===> 1.定义User类,实现Serializable接口。

public class User implements Serializable {
    private String name;
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

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

===> 2.测试序列化。

public class TestSerializable {
    public static void main(String[] args) {
        User user = new User("张三", 30);
        // 序列化
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("1.txt"));
            objectOutputStream.writeObject(user);
            objectOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        User user2 = null;
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("1.txt"));
            user2 = (User) objectInputStream.readObject();
            objectInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 反序列化后的内容
        System.out.println(user2.toString());
        // 反序列化后的对象已经不是原来那个
        System.out.println(user2.equals(user));
    }
}

===> 3.结果如下。
屏幕快照 2021-03-24 上午9.31.55.png
通过这个例子,我们可以清楚看到,对象序列化后,变成了一串字节序列,存到了文件里。
再次去读文件内容,又可以得到一个新的User对象,虽然不是原来那个,但是里面的数据状态是一致的。

最后再说一个点。

一般在序列化时,我们都会在类中定义一个serialVersionUID,如下,
private static final long serialVersionUID = 1L;
这个值可以把它看作key,用来处理版本兼容的。

不显式定义这个属性值的坏处

  1. 不利于程序在不同的JVM之间的移植。因为不同的编译器实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,出现因类版本不兼容而无法正确反序列化的现象出现。
  1. 如果你把对象写到了文件里,然后你又修改了对象。然后,再用新的对象去读旧文件里的对象就会报错。

屏幕快照 2021-03-24 上午10.28.05.png

显示定义后的效果

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private Integer age2; // 修改 age ==> age2
    private String gender; // 增加 gender

    public User(String name, Integer age, String gender) {
        this.name = name;
        this.age2 = age2;
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age2=" + age2 +
                ", gender='" + gender + '\'' +
                '}';
    }
}

输出:
屏幕快照 2021-03-24 上午10.38.45.png

4 小结

  Java的序列化、反序列化最终目的就是让对象变成另一种易于存储易于传输的形式,且又能识别还原回来。
把握这一核心点,用好序列化指日可待。