【Java 基础篇】Java 对象流与序列化

2023-10-12 10:21:39 浏览数 (1)

导言

在 Java 编程中,对象的序列化是指将对象转换为字节序列,以便可以将其存储到文件、内存中进行传输或在网络上进行传输。反序列化则是将字节序列重新转换为对象。Java 提供了对象流来实现对象的序列化和反序列化操作。

对象流提供了一种方便的方式来处理对象的输入和输出,它是由 ObjectInputStream 和 ObjectOutputStream 两个类组成。本文将介绍 Java 对象流的使用方法,并提供示例代码来帮助你理解其使用。

一、Java 对象流与序列化的 API

Java 提供了一组 API 来支持对象流与序列化的操作。以下是一些常用的类和接口:

1. java.io.Serializable 接口

Serializable 接口是一个标记接口,用于标识一个类可以被序列化。它没有任何方法,只是用于告诉编译器该类可以进行对象序列化。

2. java.io.ObjectOutputStream 类

ObjectOutputStream 类用于将对象序列化为字节流。它提供了一系列的 write 方法来将各种类型的数据写入输出流中,包括基本数据类型、对象和数组。一些常用的方法包括:

  • writeObject(Object obj):将对象写入输出流。
  • writeInt(int val):将一个 int 值写入输出流。
  • writeBoolean(boolean val):将一个 boolean 值写入输出流。
  • writeBytes(String str):将一个字符串以字节数组的形式写入输出流。
3. java.io.ObjectInputStream 类

ObjectInputStream 类用于从字节流中反序列化对象。它提供了一系列的 read 方法来从输入流中读取各种类型的数据。一些常用的方法包括:

  • readObject():从输入流中读取一个对象。
  • readInt():从输入流中读取一个 int 值。
  • readBoolean():从输入流中读取一个 boolean 值。
  • readBytes(byte[] buf):从输入流中读取字节数组并存储到指定的字节数组中。
4. transient 关键字

transient 是一个修饰符关键字,用于标记类的字段,表示在对象序列化时忽略该字段。被标记为 transient 的字段不会被写入输出流,也不会被反序列化。

5. serialVersionUID 字段

serialVersionUID 是一个类的序列化版本号。它是一个长整型常量,用于标识序列化类的版本。当进行反序列化时,Java 会使用对象的 serialVersionUID 和存储在序列化数据中的版本号进行比较,如果不匹配则会抛出 InvalidClassException 异常。

6. Externalizable 接口

除了实现 Serializable 接口,类还可以实现 Externalizable 接口来自定义对象的序列化和反序列化过程。Externalizable 接口继承自 Serializable 接口,并要求实现 writeExternal(ObjectOutput out)readExternal(ObjectInput in) 方法来定义对象的序列化和反序列化方式。

二、对象序列化

Java 对象序列化的过程是将对象转换为字节序列的过程,以便可以将其写入文件或传输到其他地方。序列化过程需要使用 ObjectOutputStream 类。

以下是一个简单的示例,展示了如何将一个对象序列化并写入文件:

代码语言:javascript复制
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializationExample {
    public static void main(String[] args) {
        String fileName = "object.ser";
        
        // 创建对象
        Person person = new Person("John", 25);
        
        try (ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(fileName))) {
            // 序列化对象
            outputStream.writeObject(person);
            
            System.out.println("Object serialized successfully!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们创建了一个名为 Person 的自定义类,并在 main 方法中实例化了一个 Person 对象。然后,我们创建了一个 ObjectOutputStream 对象,并将其与一个输出流 (FileOutputStream) 关联,以便将序列化数据写入文件。最后,我们调用 writeObject() 方法将 person 对象序列化并写入文件中。

通过以上代码,我们可以将 Person 对象序列化并保存到文件中。

三、对象反序列化

对象反序列化是将字节序列转换回对象的过程。Java 提供了 ObjectInputStream 类来实现对象的反序列化。

以下是一个示例代码,展示了如何从文件中读取序列化的对象并进行反序列化:

代码语言:javascript复制
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializationExample {
    public static void main(String[] args) {
        String fileName = "object.ser";
        
        try (ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(fileName))) {
            // 反序列化对象
            Person person = (Person) inputStream.readObject();
            
            System.out.println("Name: "   person.getName());
            System.out.println("Age: "   person.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们创建了一个 ObjectInputStream 对象,并将其与一个输入流 (FileInputStream) 关联,以便从文件中读取序列化的对象。然后,我们调用 readObject() 方法来读取对象并将其转换为 Person 类型。

通过以上代码,我们可以从文件中读取序列化的对象并进行反序列化。

四、序列化与反序列化的注意事项

在进行对象的序列化和反序列化时,需要注意以下几个方面:

1. 序列化 ID

每个可序列化的类都应该声明一个 serialVersionUID 字段。这个字段用于确定序列化对象的版本号。当进行反序列化时,Java 会使用对象的 serialVersionUID 和存储在序列化数据中的版本号进行比较,如果不匹配则会抛出 InvalidClassException 异常。

2. transient 关键字

在某些情况下,你可能希望对象的某些字段不被序列化,例如敏感信息或临时计算结果。你可以使用 transient 关键字来标记这些字段,以便在序列化过程中被忽略。

3. Serializable 接口

要使一个类可序列化,该类必须实现 java.io.Serializable 接口。这个接口是一个标记接口,没有任何方法定义。它只是告诉编译器该类可以进行对象序列化。

4. 对象引用

当对象进行序列化时,如果对象引用其他对象,则该对象引用的对象也必须是可序列化的,否则会抛出 NotSerializableException 异常。

结语

本文介绍了 Java 对象流与序列化的基本概念和使用方法。通过对象流,我们可以方便地将对象序列化并写入文件,也可以从文件中读取序列化的对象进行反序列化。序列化和反序列化在数据持久化、网络传输等场景中都起到了重要的作用。

希望本文对你理解 Java 对象流与序列化有所帮助。祝你在 Java 编程中取得更多的成功!

0 人点赞