拓展知识四:序列化与反序列化(Serialization and Deserialization)

前提概要

本文章主要用于分享序列化与反序列化基础学习,以下是对序列化与反序列化的一些个人解析,请大家结合参考其他文章中的相关信息进行归纳和补充。


序列化与反序列化描述

        序列化将对象转换为字节序列的过程(可以传输或存储的形式),它使对象能在网络中传输,或者以文件形式持久化存储。这就好比把复杂的物品打包以便运输或存放。反序列化则是序列化的逆过程把字节序列恢复为原始对象,如同将打包物品重新还原。序列化和反序列化在多种场景中应用广泛,例如网络通信时,发送方先对数据对象进行序列化,接收方再进行反序列化来获取原始数据;数据存储时,将对象序列化保存到文件或数据库,后续使用时再反序列化还原。

        不同编程语言有各自实现序列化和反序列化的方法,像 Python 用 pickle 模块,Java 借助 Serializable 接口,JavaScript 常用 JSON.stringify() 和 JSON.parse()

        对象 -- 序列化 --> 字符串 -- 反序列化 --> 对象

 接下来我用问答的方式帮助你了解序列化与反序列化

什么是序列化和反序列化?

        序列化是指将内存中的对象转换为一种可以存储或传输的格式(通常是字节序列)的过程。反序列化则是序列化的逆过程,它将存储或传输的字节序列重新转换为内存中的对象。

为什么要进行序列化和反序列化?

  • 数据持久化:内存中的对象在程序结束后就会消失,若要长期保存对象的状态,就需要将其序列化后存储到磁盘文件、数据库等持久化存储介质中。当需要使用时,再通过反序列化恢复对象。
  • 网络传输:在网络通信中,不同计算机之间无法直接传输内存中的对象,需要将对象序列化为字节流后通过网络传输,接收方再进行反序列化得到原始对象。

为什么对象需要序列化反序列化,不能直接存储?

 在计算机编程中,对象通常不能直接存储主要有以下几个原因:

存储方面

        不同的存储地方(像文件、数据库)有自己规定的数据格式。对象在程序里是按自己的方式存在的,和存储地方要求的格式不一样。比如,你不能把一件立体的雕塑原样塞进一个只能放平面照片的相册,得把雕塑拍成照片(就像把对象序列化)才能放进去,之后想看雕塑样子时,再根据照片想象出雕塑原本的样子(这就是反序列化)。

数据关系方面

        对象之间可能有很多关联,就像一群人之间有亲戚关系一样。直接存储对象,这些关联关系就可能丢失或者混乱。序列化的时候会把这些关系记录下来,反序列化时就能恢复正确的关联。比如,序列化时记录了 “张三的领导是李四”,反序列化后还能知道他们的上下级关系。

传输方面

        在网络上传对象时,对象里有很多不必要的信息,直接传会占很多网络空间,速度也慢。序列化能把对象精简成紧凑的信息包,就像把一个大包裹里不必要的东西拿掉,让包裹变小,这样传起来又快又省空间。接收方收到后,再通过反序列化把这个信息包变回原来的对象。

为什么序列化一个类的时候只序列化对象不序列化方法

在序列化一个类时只序列化对象而不序列化方法,主要有以下几个原因:

 
  • 方法的通用性:方法是类的一部分,它们定义了类的行为。对于同一类的不同对象,方法是相同的,不需要重复序列化。在反序列化时,类的定义(包括方法)已经存在于目标环境中,只需要将对象的状态恢复,就可以通过调用相应的方法来实现对象的行为。
  • 减少数据量:序列化的主要目的是将对象的状态保存下来,以便在需要时能够恢复对象。方法本身并不包含对象的状态信息,如果将方法也序列化,会增加大量不必要的数据量,降低序列化和反序列化的效率,也占用更多的存储空间和网络带宽。
  • 跨语言和平台兼容性:不同的编程语言和平台对于方法的表示和调用方式可能不同。如果将方法序列化,可能会导致在不同语言或平台之间进行反序列化时出现兼容性问题。而只序列化对象的状态,可以将对象的状态数据以一种通用的格式保存下来,方便在不同的环境中进行反序列化和恢复对象。

序列化和反序列化在哪些场景下会用到?

  • 缓存:在分布式系统中,为了提高性能,会将一些常用的对象序列化后存储在缓存中(如 Redis),下次使用时直接从缓存中反序列化获取对象,避免重复计算。
  • 远程方法调用(RPC):当一个程序需要调用另一个远程程序的方法时,需要将调用的参数和方法信息序列化后发送到远程服务器,服务器接收到后进行反序列化,执行相应的方法,并将结果序列化返回给调用方。
  • 数据备份与恢复:对系统中的重要数据对象进行序列化后保存到外部存储设备,在需要时可以通过反序列化恢复数据。

序列化与反序列化在不同编程语言中是如何实现的?

PHP 里怎样进行序列化和反序列化?

        在 PHP 里,可使用 serialize() 和 unserialize() 函数分别进行序列化和反序列化操作。

基本用法

  • serialize():此函数会把一个 PHP 变量转换为可以存储或传输的字符串。该字符串能够完整地记录变量的类型和值。
  • unserialize():它的作用是将 serialize() 函数生成的字符串重新转换为 PHP 变量。

示例代码:

序列化和反序列化数组

<?php
// 定义一个数组
$myArray = array('name' => 'John', 'age' => 30, 'hobbies' => array('reading', 'running'));

// 序列化数组
$serializedArray = serialize($myArray);
echo "序列化后的字符串: ". $serializedArray. "<br>";

// 反序列化数组
$unserializedArray = unserialize($serializedArray);
print_r($unserializedArray);
?>

序列化和反序列化对象

<?php
// 定义一个类
class Person {
    public $name;
    public $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

// 创建一个对象
$person = new Person('Jane', 25);

// 序列化对象
$serializedObject = serialize($person);
echo "序列化后的字符串: ". $serializedObject. "<br>";

// 反序列化对象
$unserializedObject = unserialize($serializedObject);
echo "姓名: ". $unserializedObject->name. ", 年龄: ". $unserializedObject->age;
?>

注意事项

  • 类定义的一致性:若要反序列化一个对象,反序列化时类的定义必须和序列化时一致。若类的定义有改变,可能会造成反序列化失败或者得到不完整的对象。

  • 安全性问题:反序列化不可信的数据会有安全风险,因为攻击者可能会构造恶意的序列化字符串来执行任意代码。所以,只对来自可信来源的数据进行反序列化操作。

  • 数据类型支持serialize() 能处理大部分 PHP 数据类型,如数组、对象、字符串、整数等。不过,某些特殊类型(像资源类型)无法被序列化。

Python 里怎样进行序列化和反序列化?

        Python 可使用 pickle 模块来实现。下面是一个简单示例:

import pickle

# 定义一个对象
data = {'name': 'Alice', 'age': 25}

# 序列化对象
serialized = pickle.dumps(data)

# 反序列化对象
deserialized = pickle.loads(serialized)

print(deserialized)

 在这个示例中,pickle.dumps() 把对象序列化为字节流,pickle.loads() 把字节流反序列化为对象。

Java 中怎么实现序列化和反序列化?

        Java 里要让类实现 Serializable 接口来实现序列化和反序列化。示例如下:

import java.io.*;

// 定义一个可序列化的类
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;
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        try {
            // 创建一个 Person 对象
            Person person = new Person("Bob", 30);

            // 序列化对象到文件
            FileOutputStream fileOut = new FileOutputStream("person.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(person);
            out.close();
            fileOut.close();

            // 从文件反序列化对象
            FileInputStream fileIn = new FileInputStream("person.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            Person deserializedPerson = (Person) in.readObject();
            in.close();
            fileIn.close();

            System.out.println("Name: " + deserializedPerson.getName());
            System.out.println("Age: " + deserializedPerson.getAge());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

     在这个 Java 示例中,Person 类实现了 Serializable 接口,使用 ObjectOutputStream 进行序列化,用 ObjectInputStream 进行反序列化。

 JavaScript 中如何进行序列化和反序列化?

        JavaScript 通常用 JSON.stringify() 来序列化对象,用 JSON.parse() 来反序列化。示例如下:

// 定义一个对象
const data = { name: 'Charlie', age: 35 };

// 序列化对象
const serialized = JSON.stringify(data);

// 反序列化对象
const deserialized = JSON.parse(serialized);

console.log(deserialized);

        在这个 JavaScript 示例中,JSON.stringify() 把对象转换为 JSON 字符串,JSON.parse() 把 JSON 字符串转换回对象。

序列化和反序列化有哪些注意事项?

  • 版本兼容性:如果对象的类结构在序列化和反序列化之间发生了变化,可能会导致反序列化失败。在 Java 中,可以通过定义 serialVersionUID 来确保版本兼容性。
  • 安全性:反序列化过程可能存在安全风险,如反序列化恶意构造的字节流可能导致代码执行漏洞。在处理外部输入的序列化数据时,要确保数据来源的安全性。
  • 性能问题:序列化和反序列化过程可能会消耗一定的时间和资源,特别是对于复杂的对象或大量数据。在性能敏感的场景中,需要考虑优化序列化和反序列化的方式。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值