Java中的序列化和反序列化

什么是序列化和反序列化

序列化(Serialization)和反序列化(Deserialization)是指在计算机科学中将对象转换为字节流的过程以及将字节流重新转换为对象的过程。

序列化是将对象的状态转换为字节流的过程,以便存储到磁盘、通过网络传输或在内存中进行持久化。通过序列化,对象可以被保存或传输,并且可以在需要时重新还原为原始对象。

反序列化是将字节流转换为对象的过程。通过反序列化,我们可以从字节流中恢复出之前序列化的对象,并重新构建该对象的状态。这使得我们可以在不同的计算机、进程或网络中传递对象,并在需要时重新还原。

序列化和反序列化经常用于分布式系统、持久化存储、缓存、进程间通信等场景。它们使得对象可以以紧凑的二进制形式进行存储和传输,方便在不同环境中使用和共享对象。

实现方式(推荐前两种)

1.Java 原生的 Serializable 接口实现实体类序列化

你可以使用 Java 的 `ObjectOutputStream` 和 `ObjectInputStream` 类来进行实体类的序列化和反序列化。以下是一个封装了实体类序列化和反序列化方法的示例代码:


import java.io.*;

public class SerializationUtil {
    // 将对象序列化为字节数组
    public static byte[] serialize(Object object) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
            objectOutputStream.writeObject(object);
            return byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    // 将字节数组反序列化为对象
    public static Object deserialize(byte[] data) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
            return objectInputStream.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

在这个示例中,`SerializationUtil` 类封装了两个静态方法。`serialize` 方法接收一个对象作为参数,将该对象序列化为一个字节数组并返回。`deserialize` 方法接收一个字节数组作为参数,将该字节数组反序列化为一个对象并返回。

要使用这些方法,只需调用对应方法并传入相应的参数即可。例如:


Person person = new Person("张三", 20);

// 对象序列化
byte[] data = SerializationUtil.serialize(person);

// 对象反序列化
Person deserializedPerson = (Person) SerializationUtil.deserialize(data);

上述代码中,我们创建了一个 `Person` 对象,并将其序列化为字节数组。然后,我们再将这个字节数组反序列化为一个新的 `Person` 对象 `deserializedPerson`。

需要注意的是,被序列化的对象必须实现 `Serializable` 接口,并且所有字段都应该是可序列化的。同时,要确保序列化和反序列化使用相同的类加载器和类结构,否则可能会导致反序列化失败。此外,Java 原生的序列化方式并不适用于大规模数据和高性能场景,因此在实际应用中,推荐使用其他高效的序列化框架,如 Avro、Protobuf ,Jackson 等。

2.使用 Jackson 库实现序列化和反序列化:

首先,确保你已经在项目中引入了 Jackson 库的依赖。

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

然后,你可以使用 Jackson 的 ObjectMapper 类来实现序列化和反序列化。以下是示例代码:

import com.fasterxml.jackson.databind.ObjectMapper;

public class SerializationUtil {
    private static ObjectMapper objectMapper = new ObjectMapper();

    // 将对象序列化为 JSON 字符串
    public static String serialize(Object object) throws IOException {
        return objectMapper.writeValueAsString(object);
    }

    // 将 JSON 字符串反序列化为对象
    public static <T> T deserialize(String json, Class<T> valueType) throws IOException {
        return objectMapper.readValue(json, valueType);
    }
}

在上述代码中,我们使用 Jackson 提供的 ObjectMapper 类来进行序列化和反序列化操作。serialize 方法将一个对象序列化为 JSON 字符串,并返回该字符串。deserialize 方法接收一个 JSON 字符串和目标对象的类型,将 JSON 字符串反序列化为对应的对象并返回。

使用实例:

Person person = new Person("张三", 20);

// 对象序列化
String json = SerializationUtil.serialize(person);

// 对象反序列化
Person deserializedPerson = SerializationUtil.deserialize(json, Person.class);

3.使用 Google 的 Protobuf 库实现序列化和反序列化:

首先,确保你已经在项目中引入了 Protobuf 库的依赖。

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.17.3</version>
</dependency>

定义 .proto 文件来描述你的数据结构:

syntax = "proto3";

message Person {
    string name = 1;
    int32 age = 2;
}

然后,使用 Protobuf 提供的代码生成工具(protoc)根据 .proto 文件生成相应的 Java 类。在生成的 Java 类中,Person 类提供了 toByteArray() 方法用于序列化,以及 parseFrom(byte[] data) 方法用于反序列化。

public class SerializationUtil {
    // 将对象序列化为字节数组
    public static byte[] serialize(Person person) {
        return person.toByteArray();
    }

    // 将字节数组反序列化为对象
    public static Person deserialize(byte[] data) throws InvalidProtocolBufferException {
        return Person.parseFrom(data);
    }
}

使用实例:

Person person = Person.newBuilder()
        .setName("张三")
        .setAge(20)
        .build();

// 对象序列化
byte[] data = SerializationUtil.serialize(person);

// 对象反序列化
Person deserializedPerson = SerializationUtil.deserialize(data);

4.用avro实现实体类的序列化和反序列化

定义 Avro Schema(模式): 首先,你需要定义一个 Avro Schema 来描述你的数据结构。Schema 可以使用 Avro 的 JSON 或者编程接口来定义。

例如,假设你有一个 Person 类,你可以创建一个名为 person.avsc 的文件,其中包含以下内容:

{
  "type": "record",
  "name": "Person",
  "fields": [
    {"name": "name", "type": "string"},
    {"name": "age", "type": "int"}
  ]
}
  1. 通过 Avro 工具生成 Java 类:然后,你需要使用 Avro 工具将 Avro Schema 文件转换成相应的 Java 类。你可以使用命令行工具 avro-tools.jar 来完成这一步。
java -jar avro-tools.jar compile schema person.avsc ./output/

这将会生成名为 Person.java 的 Java 类文件,位于 ./output/ 目录下。 

    2.使用生成的 Java 类进行序列化和反序列化:接下来,你可以使用生成的 Java 类来进行实体类的序列化和反序列化。

import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericDatumWriter;
import org.apache.avro.io.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class SerializationUtil {
    private static Schema schema; // Avro Schema

    static {
        try {
            schema = new Schema.Parser().parse(SerializationUtil.class.getResourceAsStream("/output/person.avsc"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 将对象序列化为字节数组
    public static byte[] serialize(Person person) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        DatumWriter<GenericData.Record> datumWriter = new GenericDatumWriter<>(schema);
        Encoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null);

        GenericData.Record avroRecord = new GenericData.Record(schema);
        avroRecord.put("name", person.getName());
        avroRecord.put("age", person.getAge());

        datumWriter.write(avroRecord, encoder);
        encoder.flush();
        outputStream.close();

        return outputStream.toByteArray();
    }

    // 将字节数组反序列化为对象
    public static Person deserialize(byte[] data) throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
        DatumReader<GenericData.Record> datumReader = new GenericDatumReader<>(schema);
        Decoder decoder = DecoderFactory.get().binaryDecoder(inputStream, null);

        GenericData.Record avroRecord = datumReader.read(null, decoder);
        inputStream.close();

        return new Person(
                avroRecord.get("name").toString(),
                (int) avroRecord.get("age")
        );
    }
}

在上述代码中,我们使用 Apache Avro 的相关类来实现序列化和反序列化操作。首先,我们加载 Avro Schema 文件(person.avsc),然后根据 Schema 创建 GenericData.Record 对象来表示数据,并使用 GenericDatumWriterGenericDatumReader 进行序列化和反序列化。

使用实例:

Person person = new Person("张三", 20);

// 对象序列化
byte[] data = SerializationUtil.serialize(person);

// 对象反序列化
Person deserializedPerson = SerializationUtil.deserialize(data);

这样,你就可以使用 Avro 实现实体类的序列化和反序列化了。记得将 Avro Schema 文件和生成的 Java 类文件添加到你的项目中。

要去除Avro记录(records)中的模式信息,您可以使用Avro工具库或编程语言的Avro库进行处理。以下是使用Java Avro库的示例代码: ```java import org.apache.avro.Schema; import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericDatumReader; import org.apache.avro.generic.GenericDatumWriter; import org.apache.avro.generic.GenericRecord; import org.apache.avro.io.*; import org.apache.avro.util.ByteBufferInputStream; import org.apache.avro.util.ByteBufferOutputStream; import java.io.IOException; public class RemoveSchemaFromAvroRecord { public static void main(String[] args) throws IOException { // 原始Avro记录(record)的字节数据 byte[] avroBytes = ...; // 从源数据中获取原始Avro字节数据 // 读取字节数据中的模式信息 DatumReader<GenericRecord> reader = new GenericDatumReader<>(); BinaryDecoder decoder = DecoderFactory.get().binaryDecoder(new ByteBufferInputStream(ByteBuffer.wrap(avroBytes)), null); GenericRecord avroRecord = reader.read(null, decoder); // 创建新的不包含模式信息的Avro记录 GenericRecord newRecord = new GenericData.Record(avroRecord.getSchema()); for (Schema.Field field : avroRecord.getSchema().getFields()) { newRecord.put(field.name(), avroRecord.get(field.name())); } // 将新记录写入字节流中 DatumWriter<GenericRecord> writer = new GenericDatumWriter<>(avroRecord.getSchema()); ByteBufferOutputStream outputStream = new ByteBufferOutputStream(); Encoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null); writer.write(newRecord, encoder); encoder.flush(); // 获取不包含模式信息的Avro字节数据 byte[] newAvroBytes = outputStream.getBuffer().array(); // 使用新的Avro字节数据进行后续处理 // ... } } ``` 在上述示例中,我们使用Avro库解码原始Avro字节数据,并创建一个新的Avro记录,只包含字段值而不包含模式信息。然后,我们将新记录编码为字节流,并使用这个不包含模式信息的Avro字节数据进行后续处理。 请注意,上述示例代码是使用Java编写的,并使用Apache Avro库进行操作。如果您使用其他编程语言,请参考相应语言的Avro库文档,了解如何读取写入Avro记录,并根据需要去除模式信息。 希望这个示例能对您有所帮助!如有任何进一步的问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值