序列化:将java对象转化为字节序列的过程。

反序列化:将字节序列转化为java对象的过程。

        在进行远程通信时,如果需要传输java对象:发送方需要把java对象转换为字节序列(也就是序列化),接收方需要将字节序列转换为java对象(也就是反序列化)。

        序列化的好处是实现了数据的持久化,通过序列化可以将数据永久地保存到硬盘上,也可以将对象进行网络传输。

        java.io.ObjectOutputStream表示对象输出流,writeObject(Object obj)方法可以将obj对象序列化,把得到的字节序列写入输出流中。

        java.io.ObjectInputStream表示对象输入流,readObject()方法输入流中读取字节序列,再把它们反序列化成一个对象,并返回。

        只有实现了Seriizable或Externalizable接口的类对象才能被序列化,否则会抛出异常。

序列化与反序列化功能的代码

/**
 * 序列化与反序列化
 *
 * @author hu
 * @date 2023/02/02 11:22
 */
public class SerialTest {
    // 序列化
    public static void serialize() throws IOException {

        // 声明Person对象
        Person person = new Person();
        person.setName("hu");
        person.setAge(18);
        person.setScore(1000);

        // 序列化并持久化到person.txt文件中
        ObjectOutputStream objectOutputStream =
                new ObjectOutputStream(new FileOutputStream(new File("person.txt")));
        objectOutputStream.writeObject(person);
        objectOutputStream.close();

        System.out.println("序列化成功!已经生成person.txt文件");
        System.out.println("==============================================");
    }

    // 反序列化
    public static void deserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream =
                new ObjectInputStream(new FileInputStream(new File("person.txt")));
        Person person = (Person) objectInputStream.readObject();
        objectInputStream.close();

        System.out.println("反序列化结果为:");
        System.out.println(person);
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        serialize();
        deserialize();
    }
    
}

class Person implements Serializable {
    private String name;
    private Integer age;
    private Integer score;

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }
}

Serializable接口的作用

        Serializable接口并没有任何方法,只是一个声明式接口,标记类是否可以序列化和反序列化。

serialVersionUID的作用

        反序列化需要serialVersionUID来判断版本是否一致。如果序列化的serialVersionUID与反序列化的serialVersionUID不同则无法完成反序列化。只有相同才能完成反序列化。类似于解决乐观锁ABA问题的version。

怎样排除敏感字段

        可能遇到的场景就是在序列化时有些敏感字段不方便被保留,比如用户密码,会造成安全性问题。这时候我们该怎么办呢?

        使用transtient修饰不想序列化的字段

                transtient修饰符是标记不需要持久化的字段。

        使用Externalizable接口

        Externalizable接口继承与Serializable接口,目的是满足可扩展性,让你自行实现序列化逻辑。

        在学习序列化相关知识的时候,看到有人说使用static修饰的字段也可以达到序列化屏蔽字段的效果。但是本人在测试的时候并没有体现出来。有大佬懂的话,也希望给小弟解解惑。

class Person implements Serializable {
    private String name;
    private static Integer age;
    private transient Integer score;

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        Person.age = age;
    }

    public Integer getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score = score;
    }
}

====================================================

        不得不承认个人犯了一个蠢,在验证如何排除敏感字段时,其实有个使用static关键字修饰成员变量,但验证的时候发现反序列化能输出正确值,这里忽略了static的语义,其实输出时输出的是类的静态成员值,而不是从文件中序列化出来的值。特此强调一下,避免他人走同样的弯路。