序列化与反序列化
序列化是将对象转换为可以存储或传输的格式的过程。在计算机科学中,对象通常是指内存中的数据结构,如数组、列表、字典等。通过序列化,可以将这些对象转换为字节流或文本格式,以便在不同的系统之间进行传输或存储。序列化后的数据可以被传输到远程系统,或者存储在磁盘上,以便在需要时进行读取和恢复。序列化的逆过程称为反序列化,即将序列化后的数据重新转换为原始对象的过程。
反序列化是将序列化后的数据恢复为原始对象的过程。在编程中,我们经常需要将对象序列化为字节流或者其他形式的数据,以便在网络传输或者持久化存储中使用。而反序列化则是将这些序列化后的数据重新转换为原始对象。
在不同的编程语言中,反序列化的实现方式可能会有所不同。一般来说,反序列化的过程包括以下几个步骤:
- 读取序列化后的数据:从文件、网络传输等地方读取序列化后的数据。
- 解析数据:根据序列化的格式,解析数据并还原为原始的对象结构。
- 创建对象:根据解析得到的数据,创建对应的对象实例。
- 恢复对象状态:将解析得到的数据赋值给对象的属性,恢复对象的状态。
反序列化的过程可以用以下伪代码表示:
ini
data = 读取序列化后的数据
object = 解析数据(data)
在实际应用中,反序列化的方式和具体实现会根据编程语言和序列化库的不同而有所差异。不同的序列化格式有不同的特点和适用场景,开发者可以根据具体需求选择合适的序列化方式。
Android数据对象序列化的用途
Android数据对象序列化的主要用途是将对象转换为字节流的形式,以便在网络传输、持久化存储或进程间通信中使用。具体的用途包括:
-
网络传输:在Android开发中,我们经常需要将对象通过网络传输给其他设备或服务器。通过序列化,我们可以将对象转换为字节流,然后通过网络发送给目标设备或服务器,目标设备或服务器再将字节流反序列化为对象进行处理。
-
持久化存储:Android应用程序通常需要将数据保存在本地存储中,以便在应用程序关闭后仍然可以访问。通过序列化,我们可以将对象转换为字节流,并将其保存在本地文件或数据库中。当应用程序再次启动时,我们可以将字节流反序列化为对象,以便恢复之前保存的数据。
-
进程间通信:在Android中,不同的组件(如Activity、Service、BroadcastReceiver等)可能运行在不同的进程中。通过序列化,我们可以将对象转换为字节流,并通过进程间通信机制(如Binder)将字节流传递给其他进程,其他进程再将字节流反序列化为对象进行处理。
序列化提供了一种方便的方式来在不同的场景中传输和存储对象数据。它在网络传输、持久化存储和进程间通信等方面都有广泛的应用。
Android实现对象序列化的方式
在Android中,常用的实现对象序列化有以下几种方式:
- 实现Serializable接口:在需要序列化的类中实现Serializable接口,该接口没有任何方法,只是作为一个标记接口。然后使用ObjectOutputStream将对象写入输出流,使用ObjectInputStream从输入流中读取对象。示例代码如下:
java
public class MyClass implements Serializable {
// 类的成员变量和方法
public static void main(String[] args) {
// 序列化对象
MyClass obj = new MyClass();
try {
FileOutputStream fileOut = new FileOutputStream("object.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(obj);
out.close();
fileOut.close();
System.out.println("对象已序列化");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化对象
MyClass newObj = null;
try {
FileInputStream fileIn = new FileInputStream("object.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
newObj = (MyClass) in.readObject();
in.close();
fileIn.close();
System.out.println("对象已反序列化");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
- 实现Parcelable接口:Parcelable接口是Android特有的接口,相比Serializable接口,它更高效。在需要序列化的类中实现Parcelable接口,并实现相关方法。然后使用Parcel对象将对象写入Parcel,使用Parcel对象从Parcel中读取对象。示例代码如下:
java
public class MyClass implements Parcelable {
// 类的成员变量和方法
protected MyClass(Parcel in) {
// 从Parcel中读取数据并赋值给成员变量
}
public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
@Override
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
@Override
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// 将成员变量写入Parcel
}
}
- 使用Gson库:Gson是Google提供的一个用于在Java对象和JSON数据之间进行序列化和反序列化的库。可以使用Gson将对象转换为JSON字符串,然后再将JSON字符串转换为对象。示例代码如下:
java
public class MyClass {
// 类的成员变量和方法
public static void main(String[] args) {
// 序列化对象
MyClass obj = new MyClass();
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println("对象已序列化为JSON字符串:" + json);
// 反序列化对象
MyClass newObj = gson.fromJson(json, MyClass.class);
System.out.println("JSON字符串已反序列化为对象");
}
}
序列化原理
Serializable是Java中的一个接口,用于实现对象的序列化和反序列化。序列化是指将对象转换为字节流的过程,而反序列化则是将字节流转换为对象的过程。
Serializable接口没有任何方法,它只是一个标记接口,用于告诉Java虚拟机,该类可以被序列化。要实现序列化,只需要让类实现Serializable接口即可。
在序列化过程中,Java虚拟机会将对象的状态转换为字节序列,然后可以将字节序列保存到文件、数据库或通过网络传输。反序列化过程则是将字节序列重新转换为对象的状态。
在序列化过程中,Java虚拟机会对对象的各个字段进行序列化。对于基本类型和引用类型,Java虚拟机会自动进行序列化。对于自定义类型,需要实现Serializable接口,并且保证该类型的所有成员变量也是可序列化的。
在反序列化过程中,Java虚拟机会根据字节序列重新创建对象,并将字节序列中的数据赋值给对象的各个字段。
需要注意的是,序列化和反序列化的过程中,对象的构造函数不会被调用。因此,在反序列化过程中,如果需要进行一些初始化操作,可以使用特殊的方法readObject()来实现。
总结起来,Serializable接口提供了一种简单的方式来实现对象的序列化和反序列化。通过实现Serializable接口,可以将对象转换为字节序列,以便在不同的环境中进行传输和存储。
Parcelable是Android中用于实现对象序列化的接口。它的原理是将对象的数据按照一定的格式进行打包和解包,以便在不同的组件之间传输或存储。
具体实现步骤如下:
-
实现Parcelable接口:在需要序列化的类中实现Parcelable接口,并实现其中的方法,包括
describeContents()
和writeToParcel(Parcel dest, int flags)
。 -
describeContents()
方法:该方法返回一个标志位,用于描述Parcelable对象特殊对象的类型。一般情况下,返回0即可。 -
writeToParcel(Parcel dest, int flags)
方法:该方法将对象的数据写入Parcel对象中。在该方法中,需要将对象的各个字段按照一定的顺序写入Parcel对象中,以便在解包时按照相同的顺序读取。 -
实现Parcelable.Creator接口:在需要序列化的类中实现Parcelable.Creator接口,并实现其中的方法,包括
createFromParcel(Parcel source)
和newArray(int size)
。 -
createFromParcel(Parcel source)
方法:该方法从Parcel对象中读取数据,并创建出Parcelable对象。在该方法中,需要按照写入Parcel对象时的顺序读取数据,并将其赋值给相应的字段。 -
newArray(int size)
方法:该方法返回一个指定大小的Parcelable数组。
通过实现Parcelable接口,可以将对象的数据打包成一个Parcel对象,然后可以通过Intent传递给其他组件,或者通过Bundle存储到本地。在接收端,可以通过读取Parcel对象的数据,重新构建出原始的对象。
总结起来,Parcelable的原理就是将对象的数据按照一定的格式进行打包和解包,以实现对象的序列化和反序列化。这种方式相对于Java中的Serializable接口,更加高效和灵活。
Serializable/Parcelable对比
Serializable和Parcelable都是用于实现对象的序列化和反序列化的接口,但在实现方式和性能方面有所不同。
- Serializable:
- Serializable是Java提供的默认序列化机制,通过实现Serializable接口,可以将对象转换为字节流,以便在网络传输或保存到文件中。
- Serializable使用反射机制,将对象的状态保存到字节流中,然后再从字节流中恢复对象的状态。这种方式相对简单,但效率较低。
- Serializable的缺点是序列化和反序列化的过程需要大量的I/O操作,对性能要求较高的场景下可能会影响程序的执行效率。
- Parcelable:
- Parcelable是Android提供的专门用于Android平台的序列化机制,通过实现Parcelable接口,可以将对象转换为字节流,以便在Activity之间传递。
- Parcelable使用了更加高效的序列化方式,将对象的状态拆分为多个字段,分别写入和读取字节流。这种方式相对复杂,但效率较高。
- Parcelable的优点是序列化和反序列化的过程更加高效,对性能要求较高的场景下可以提升程序的执行效率。
Serializable适用于简单的序列化场景,而Parcelable适用于对性能要求较高的Android平台。在选择使用Serializable还是Parcelable时,需要根据具体的需求和性能要求进行权衡。
数据来自parcelable-vs-serializable,实验结果对比Parcelable的效率比Serializable快10倍以上。
总结
对比 | Serializable | Parcelable |
---|---|---|
所属API | Java API | Android SDK API |
特点 | 序列化和反序列化会经过大量的I/O操作,产生大量的临时变量引起GC,且反序列化时需要反射 | 基于内存拷贝实现的封装和解封(marshalled& unmarshalled),序列化基于Native层实现 |
开销 | 相对高 | 相对低 |
效率 | 相对低 | 相对高 |
适用场景 | 简单序列化 | Android |
在使用Serializable进行对象的序列化时,有一些注意点需要注意:
-
类的定义:被序列化的类需要实现
Serializable
接口,这是Java提供的一个标记接口,用于表示该类可以被序列化。如果一个类没有实现Serializable
接口,那么在进行序列化时会抛出NotSerializableException
异常。 -
成员变量的序列化:被序列化的类的所有成员变量都会被序列化,包括私有成员变量。但是,如果某个成员变量不希望被序列化,可以使用
transient
关键字进行修饰,被修饰的成员变量在序列化过程中会被忽略。 -
对象引用的序列化:如果一个类中包含其他对象的引用,那么在序列化时,被引用的对象也会被序列化。但是,如果被引用的对象没有实现
Serializable
接口,那么在序列化时会抛出NotSerializableException
异常。为了解决这个问题,可以将被引用的对象设置为transient
,或者让被引用的对象也实现Serializable
接口。 -
序列化版本号:在进行对象的序列化时,会为每个被序列化的类自动生成一个序列化版本号。这个版本号用于在反序列化时判断序列化的类和反序列化的类是否兼容。如果序列化的类和反序列化的类的版本号不一致,会抛出
InvalidClassException
异常。为了避免这个问题,可以显式地为类指定一个固定的序列化版本号,可以使用serialVersionUID
关键字进行指定。 -
序列化的安全性:在进行对象的序列化时,需要注意序列化的安全性。因为序列化的数据可以被反序列化成对象,如果序列化的数据被篡改,可能会导致安全漏洞。为了增强序列化的安全性,可以使用加密算法对序列化的数据进行加密,或者对序列化的类进行签名验证。
在使用Parcelable进行序列化时,有几个注意点需要注意:
-
实现Parcelable接口:要使一个类可序列化,需要让该类实现Parcelable接口,并实现其中的方法。这些方法包括
writeToParcel()
和createFromParcel()
等。 -
内部类的序列化:如果要序列化的类中包含内部类,需要确保内部类也实现了Parcelable接口,并在外部类的
writeToParcel()
和createFromParcel()
方法中对内部类进行序列化和反序列化。 -
序列化顺序:在
writeToParcel()
方法中,需要按照成员变量的顺序将数据写入Parcel对象。在createFromParcel()
方法中,需要按照写入的顺序读取数据。 -
序列化和反序列化的一致性:在序列化和反序列化过程中,需要确保写入和读取的数据类型一致。例如,如果在
writeToParcel()
方法中写入了一个整数,那么在createFromParcel()
方法中读取时也需要使用相同的方法读取整数。 -
版本控制:如果在序列化的类中进行了修改,需要注意版本控制。可以通过给类添加一个版本号来实现版本控制,以便在反序列化时能够正确处理不同版本的数据。
使用Parcelable进行序列化时,需要确保实现了Parcelable接口,并注意序列化顺序、内部类的序列化、数据类型的一致性和版本控制等问题。