Java IO:序列化和反序列化

在 Java 中,序列化(Serialization)和反序列化(Deserialization)是两个重要的概念,它们主要用于将对象转换为字节流(序列化)以及将字节流恢复为对象(反序列化)。这在对象的存储、网络传输等场景中非常有用。如下图:

image.png

序列化的定义和用途

序列化是指将一个 Java 对象转换为字节序列的过程,这个字节序列可以被保存到文件、数据库,或者通过网络传输到其他地方。反序列化则是序列化的逆过程,它将字节序列恢复为原来的 Java 对象。

序列化的主要用途包括:

  • 对象持久化:将对象保存到文件、数据库中,以便后续恢复使用。

  • 远程方法调用(RMI):在分布式系统中,通过网络传输对象。

  • 对象复制:通过序列化和反序列化可以实现对象的深拷贝。

实现序列化的条件

要使一个类的对象能够被序列化,该类必须实现 java.io.Serializable 接口。Serializable 接口是一个标记接口,它没有任何方法,只是用来表明实现该接口的类可以被序列化。

以下是一个简单的可序列化类的示例:

package com.hxstrive.java_io.serializable;

import java.io.Serializable;

/**
 * 实现 Serializable 接口
 */
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

在上述代码中,Person 类实现了 Serializable 接口,并且定义了一个 serialVersionUID 常量。serialVersionUID 用于在反序列化时验证序列化对象的版本一致性,如果不指定,Java 会根据类的结构自动生成一个。

序列化对象

要将一个对象序列化,可以使用 ObjectOutputStream 类。以下是一个示例:

package com.hxstrive.java_io.serializable;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializationExample {
    public static void main(String[] args) {
        Person person = new Person("John", 30);
        try (FileOutputStream fos = new FileOutputStream("person.ser");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            // 写入对象
            oos.writeObject(person);
            System.out.println("对象已序列化");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行示例,输出:

image.png

在上述代码中,首先创建了一个 Person 对象,然后使用 FileOutputStream ObjectOutputStream 将该对象写入到名为 person.ser 的文件中。

反序列化对象

要将一个序列化的对象恢复为原来的对象,可以使用 ObjectInputStream 类。以下是一个示例:

package com.hxstrive.java_io.serializable;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializationExample {
    public static void main(String[] args) {
        final String userDir = System.getProperty("user.dir");
        try (FileInputStream fis = new FileInputStream(userDir + File.separator + "person.ser");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            // 读取对象
            Person person = (Person) ois.readObject();
            System.out.println("对象已反序列化: " + person);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

对象已反序列化: Person{name='John', age=30}

在上述代码中,使用 FileInputStream 和 ObjectInputStream 从 person.ser 文件中读取对象,并将其转换为 Person 类型。

注意事项📢

transient 关键字

如果一个类的某个字段被声明为 transient,那么在序列化时该字段将被忽略。例如:

import java.io.Serializable;

public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private transient int age; // 该字段不会被序列化

    // 构造方法、getter 和 setter 方法
}

serialVersionUID 的重要性

在进行序列化和反序列化时,建议显式地指定 serialVersionUID,以确保在类的结构发生变化时,仍然能够正确地进行反序列化。

 

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号