前提概要
本文章主要用于分享序列化与反序列化基础学习,以下是对序列化与反序列化的一些个人解析,请大家结合参考其他文章中的相关信息进行归纳和补充。
序列化与反序列化描述
序列化是将对象转换为字节序列的过程(可以传输或存储的形式),它使对象能在网络中传输,或者以文件形式持久化存储。这就好比把复杂的物品打包以便运输或存放。反序列化则是序列化的逆过程,把字节序列恢复为原始对象,如同将打包物品重新还原。序列化和反序列化在多种场景中应用广泛,例如网络通信时,发送方先对数据对象进行序列化,接收方再进行反序列化来获取原始数据;数据存储时,将对象序列化保存到文件或数据库,后续使用时再反序列化还原。
不同编程语言有各自实现序列化和反序列化的方法,像 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
来确保版本兼容性。- 安全性:反序列化过程可能存在安全风险,如反序列化恶意构造的字节流可能导致代码执行漏洞。在处理外部输入的序列化数据时,要确保数据来源的安全性。
- 性能问题:序列化和反序列化过程可能会消耗一定的时间和资源,特别是对于复杂的对象或大量数据。在性能敏感的场景中,需要考虑优化序列化和反序列化的方式。