Java 序列化与反序列化介绍

序列化(Serialization) 是将对象的状态转化为字节流的过程,而 反序列化(Deserialization) 是将字节流恢复为对象的过程。Java 提供了 Serializable 接口来支持对象的序列化操作。

1. 序列化(Serialization)

序列化是将对象转换为字节流,以便于将其保存到文件、发送到网络或者存储到数据库中。

1.1 Serializable 接口

要使一个 Java 对象能够被序列化,它必须实现 java.io.Serializable 接口。该接口是一个标记接口(没有任何方法),仅用于标识该类可以被序列化。

import java.io.Serializable;

public class Person implements Serializable {
    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;
    }
}

1.2 序列化操作

Java 提供了 ObjectOutputStream 类来实现对象的序列化。它可以将对象写入流中。

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", 25);
        
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            out.writeObject(person);
            System.out.println("Object serialized successfully.");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 在这个示例中,Person 类实现了 Serializable 接口,并通过 ObjectOutputStreamperson 对象写入文件 person.ser 中。
  • writeObject() 方法用于序列化对象。

1.3 序列化后的文件

person.ser 文件中将存储 Person 类的对象状态,这个文件可以用来将对象的状态恢复。

2. 反序列化(Deserialization)

反序列化是将字节流恢复为 Java 对象的过程。Java 提供了 ObjectInputStream 类来实现反序列化。

2.1 反序列化操作

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

public class DeserializationExample {
    public static void main(String[] args) {
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) in.readObject();  // 反序列化对象
            System.out.println("Name: " + person.getName());
            System.out.println("Age: " + person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • readObject() 方法用于从文件中读取对象并恢复。
  • 在这个示例中,person.ser 文件被读取并转换回 Person 对象。

2.2 反序列化时的注意事项

  1. 类版本号(SerialVersionUID)
    • 为了确保序列化和反序列化的兼容性,Java 使用 serialVersionUID 作为版本标识符。如果序列化时和反序列化时类的版本不一致(例如字段变化或方法变化),会导致 InvalidClassException 错误。
    • 可以显式声明 serialVersionUID,以确保反序列化时的一致性。
import java.io.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;
    }
}
  1. 转义 transient 关键字
    • 如果类中的某个字段不想被序列化,可以使用 transient 关键字。transient 修饰的字段在序列化过程中会被忽略。
import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private transient int age;  // 不会被序列化

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 在这个例子中,age 字段不会被序列化,因此在反序列化时,age 的值为默认值 0

3. 序列化和反序列化的应用场景

3.1 对象持久化

序列化可以将对象持久化到文件或数据库中,方便在之后的程序中进行恢复。这对于需要存储和恢复大量数据的应用(如缓存系统)非常有用。

3.2 网络通信

在网络编程中,序列化可以将对象通过网络传输。比如,客户端和服务器端之间可以通过序列化和反序列化传输对象。

3.3 Java RMI(远程方法调用)

Java RMI(Remote Method Invocation)利用序列化和反序列化在不同的虚拟机之间传输对象。RMI 基于远程方法调用和序列化来实现对象的传输。

3.4 深拷贝

使用序列化和反序列化可以实现对象的深拷贝(deep copy)。这对于复制复杂对象或多层次对象时特别有用。

import java.io.*;

public class DeepCopyExample {
    public static void main(String[] args) {
        try {
            Person person1 = new Person("John", 25);
            // 使用序列化和反序列化进行深拷贝
            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(byteOut);
            out.writeObject(person1);
            out.flush();
            
            ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            ObjectInputStream in = new ObjectInputStream(byteIn);
            Person person2 = (Person) in.readObject();
            
            // 修改 person2 的数据
            person2.name = "Jane";
            
            System.out.println("Person 1: " + person1.getName());  // 输出: John
            System.out.println("Person 2: " + person2.getName());  // 输出: Jane
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
  • 在这个示例中,通过序列化和反序列化实现了对象的深拷贝。修改 person2 后,person1 对象的值不受影响。

4. 序列化与反序列化的优缺点

优点缺点
易于实现,简化了对象的持久化和网络传输 反序列化过程相对较慢,性能较低
支持多种对象类型的序列化与反序列化 如果类的结构变化(如字段变动),可能会导致反序列化失败
可以实现深拷贝,复制复杂对象 序列化后的数据比原始对象更大,占用更多的存储空间
通过自定义 serialVersionUID 可以确保版本兼容 可能不适用于存储大量对象或需要高性能的场景

5. 总结

  • 序列化:将对象转换为字节流,方便存储和传输。
  • 反序列化:将字节流恢复为原始对象。
  • Serializable 接口:必须实现该接口才能进行序列化。
  • transient 关键字:标记不需要序列化的字段。
  • serialVersionUID:用于版本控制,确保序列化兼容性。
  • 应用场景:对象持久化、网络通信、深拷贝等。

建议:

  • 使用序列化时要小心类的版本变动,尽量显式声明 serialVersionUID
  • 不要在性能敏感的代码中频繁使用序列化和反序列化。
Java碎碎念 文章被收录于专栏

来一杯咖啡,聊聊Java的碎碎念呀

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务