首页 > 编程笔记 > Java笔记 阅读:4

Java对象序列化和反序列化详解(附带实例)

Java 程序中,对象的寿命通常随着程序的终止而消失。有时候需要将对象的状态保存下来,在需要时再将对象恢复。把对象这种能记录自己状态以便将来再生的能力,叫作对象的持久性(persistence)。对象通过写出描述自己状态的数值来记录自己,这个过程叫对象的序列化(Serialization)。

对象序列化机制就是把内存中的 Java 对象转换为平台无关的字节流,从而允许把这种字节流持久保存在磁盘上,通过网络将这种字节流传送到另一台主机上。其他程序一旦获得这种字节流,就可以恢复原来的对象。

如果一个对象可以被存放到磁盘上,或者可以发送到另外一台机器,并存放到存储器或磁盘上,那么这个对象就被称为可序列化的。

要序列化一个对象必须与一定的对象输入/输出流联系起来。通过对象输出流将对象状态保存,再通过对象输入流将对象状态恢复。

java.io 包中,提供了 ObjectInputStream 和 ObjectOutputStream 将数据流功能扩展至可读写对象:

Java ObjectOutputStream

ObjectOutputStream 是一个处理流,所以必须建立在其他节点流的基础之上,例如,先创建一个 FileOutputStream 输出流对象,再基于这个对象创建一个对象输出流。
FileOutputStream fileOut=new FileOutputStream(“book.txt”);
ObjectOutputStream objectOut-new ObjectOutputStream(fileOut);
writeObject() 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject() 写入。可将多个对象或基元写入流中,代码如下:
objectOut.writeObject(“Hello”);
objectOut.writeObject(new Date());
对象的默认序列化机制写入的内容:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用也会导致写入这些对象。

ObjectOutputStream 的构造方法有两个:
表:ObjectOutputStream 类的常用成员方法
方法 方法说明
void defaultWriteObject() 将当前类的非静态和非瞬态字段写入此流
void flush() 刷新该流的缓冲
void reset() 重置将丢弃已写入流中的所有对象的状态
void write(byte[] buf) 写入一个 byte 数组
void write(int val) 写入一个字节
void writeByte(int val) 写入一个 8 位字节
void writeBytes(String str) 以字节序列形式写入一个 String
void writeChar(int val) 写入一个 16 位的 char 值
void writeInt(int val) 写入一个 32 位的 int 值
void writeObject(Object obj) 将指定的对象写入ObjectOutputStream

Java ObjectInputStream

ObjectInputStream 是一个处理流,必须建立在其他节点流的基础之上。它可以对使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

示例代码如下:
FileInputStream fileIn=new FileInputStream(“book.txt”);
ObjectInputStream objectIn=new ObjectInputStream(fileIn);
readObject 方法用于从流读取对象。应该使用 Java 的安全强制转换获取所需的类型。

在 Java 中,字符串和数组都是对象,所以在序列化期间将其视为对象。读取时,需要将其强制转换为期望的类型。示例代码如下:
String s=(String)objectIn.readObject();
Date d=(Date)objectIn.readObject();
默认情况下,对象的反序列化机制会将每个字段的内容恢复为写入时它所具有的值和类型。反序列化时始终分配新对象,这样可以避免现有对象被重写。

ObjectInputStream 的构造方法有两个:
表:ObjectInputStream 类的常用方法
方法 方法说明
void defaultReadObject() 从此流读取当前类的非静态和非瞬态字段
int read() 读取数据字节
byte readByte() 读取一个 8 位的字节
char readChar() 读取一个 16 位的 char 值
int readInt() 读取一个 32 位的 int 值
ObjectStreamClass readClassDescriptor() 从序列化流读取类描述符
Object readObject() 从 ObjectInputStream 读取对象

【实例】创建了一个可序列化的学生对象,并用 ObjectOutputStream 类把它存储到一个文件(student.txt)中。然后再用 ObjectInputStream 类把存储的数据读取出来,转换成一个学生对象,即恢复保存的学生对象。
import java.io.*;
import java.util.*;

class Student implements Serializable {
    int id; // 学号
    String name; // 姓名
    int age; // 年龄
    String department; // 系列

    public Student(int id, String name, int age, String department) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.department = department;
    }
}

public class SerializableDemo {
    public static void main(String[] args) {
        Student stu1 = new Student(101036, " 刘明明 ", 18, "CSD");
        Student stu2 = new Student(101236, " 李四 ", 20, "EID ");
        File f = new File("student.txt");
        try {
            FileOutputStream fos = new FileOutputStream(f);
            // 创建一个对象输出流
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            // 把学生对象写入对象输出流中
            oos.writeObject(stu1);
            oos.writeObject(stu2);
            oos.writeObject(new Date());
            oos.close();
            FileInputStream fis = new FileInputStream(f);
            // 创建一个对象输入流,并把文件输入流对象 fis 作为源端
            ObjectInputStream ois = new ObjectInputStream(fis);
            // 把文件中保存的对象还原成对象实例
            stu1 = (Student) ois.readObject();
            stu2 = (Student) ois.readObject();
            System.out.println(" 学号=" + stu1.id);
            System.out.println(" 姓名=" + stu1.name);
            System.out.println(" 年龄=" + stu1.age);
            System.out.println(" 系列=" + stu1.department);
            System.out.println(" 学号=" + stu2.id);
            System.out.println(" 姓名=" + stu2.name);
            System.out.println(" 年龄=" + stu2.age);
            System.out.println(" 系列=" + stu2.department);
            System.out.println((Date) ois.readObject());
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
程序执行结果为:

学号=101036
姓名= 刘明明
年龄=18
系列=CSD
学号=101236
姓名= 李四
年龄=20
系列=EID
Sun May 28 12:34:56 CST 2025

在这个例子中,首先定义一个类 Student,实现了 Serializable 接口,然后通过对象输出流的 writeObject() 方法将 Student 对象保存到“student.txt”文件中。之后,通过对象输入流的 readObject() 方法从“student.txt”文件中读出保存的 Student 对象。

相关文章