一、概念
序列化和反序列化属于通讯协议的一部分,TCP/IP协议是一个四层协议,而OSI模型却是七层协议模型,TCP/IP协议的应用层对应与OSI七层协议模型的应用层,展示层和会话层,在OSI七层协议模型中展现层(Presentation Layer)的主要功能是把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象--这两个功能就是序列化和反序列化。
序列化:将数据结构或对象转换成二进制串的过程
反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
数据结构和对象:对于类似Java这种完全面向对象的语言,工程师所操作的一切都是对象(Object),来自于类的实例化。在Java语言中最接近数据结构的概念,就是POJO(Plain Old Java Object)或者Javabean--只有setter/getter方法的类。而在C++这种半面向对象的语言中,数据结构和struct对应,对象和class对应。
二进制串:序列化所生成的二进制串指的是存储在内存中的一块数据。C++语言具有内存操作符,所以二进制串的概念容易理解,例如,C++语言的字符串可以直接被传输层使用,因为其本质上就是以'\0'结尾的存储在内存中的二进制串。在Java语言里面,二进制串的概念容易和String混淆。实际上String 是Java的一等公民,是一种特殊对象(Object)。对于跨语言间的通讯,序列化后的数据当然不能是某种语言的特殊数据类型。二进制串在Java里面所指的是byte[],byte是Java的8中原生数据类型之一(Primitive data types)。
二、序列化和反序列化的组件
典型的序列化和反序列化过程往往需要如下组件:
IDL(Interfacedescription language)文件:参与通讯的各方需要对通讯的内容需要做相关的约定(Specifications)。为了建立一个与语言和平台无关的约定,这个约定需要采用与具体开发语言、平台无关的语言来进行描述。这种语言被称为接口描述语言(IDL),采用IDL撰写的协议约定称之为IDL文件。
IDL Compiler:IDL文件中约定的内容为了在各语言和平台可见,需要有一个编译器,将IDL文件转换成各语言对应的动态库。
Stub(存根)/Skeleton Lib(骨架、骨干):负责序列化和反序列化的工作代码。Stub是一段部署在分布式系统客户端的代码,一方面接收应用层的参数,并对其序列化后通过底层协议栈发送到服务端,另一方面接收服务端序列化后的结果数据,反序列化后交给客户端应用层;Skeleton部署在服务端,其功能与Stub相反,从传输层接收序列化参数,反序列化后交给服务端应用层,并将应用层的执行结果序列化后最终传送给客户端Stub。
Client/Server:指的是应用层程序代码,他们面对的是IDL所生存的特定语言的class或struct。
底层协议栈和互联网:序列化之后的数据通过底层的传输层、网络层、链路层以及物理层协议转换成数字信号在互联网中传递。
序列化组件与数据库访问组件的对比
三、java对象序列化
Java平台允许在内存中创建可复用的Java对象,但这些对象的生命周期不会比JVM的生命周期更长。在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。在Java中,对象的序列化与反序列化被广泛应用到RMI(远程方法调用)及网络传输中。
在Java中,只要一个类实现了java.io.Serializable接口,它就可以被序列化
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
privateString name;
privateint age;
privateDate birthday;
privatetransient String gender;
publicString getName() {
returnname;
}
publicvoid setName(String name) {
this.name= name;
}
publicint getAge() {
returnage;
}
publicvoid setAge(int age) {
this.age= age;
}
publicDate getBirthday() {
returnbirthday;
}
publicvoid setBirthday(Date birthday) {
this.birthday= birthday;
}
publicString getGender() {
returngender;
}
publicvoid setGender(String gender) {
this.gender= gender;
}
@Override
publicString toString(){
return "User{" +
"name='" + name +'\'' +
", age=" + age +
", gender=" + gender+
", birthday=" +birthday +
'}';
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
public class SerialzableDemo {
public static void main(String[]args){
//新建User对象
User user = new User();
user.setName("zhangsan");
user.setGender("male");
user.setAge(22);
user.setBirthday(new Date());
System.out.println(user);
//write Obj to file
ObjectOutputStream oos = null;
try{
oos = new ObjectOutputStream(newFileOutputStream("userfile"));
}catch(IOException e){
e.printStackTrace();
}finally{
IOUtils.closeQuietly(oos);
}
//Read Obj from File
File file = new File("userfile");
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(file));
User newUser = (User) ois.readObject();
System.out.println(newUser);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(ois);
try {
FileUtils.forceDelete(file);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
通过ObjectOutputStream和ObjectInputStream对对象进行序列化及反序列化
Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null
四、Android中实现序列化
1、ImplementsSerializable 接口
2、ImplementsParcelable 接口
这里仅当个人学习笔记使用,感谢并推荐以下文章,讲的很透彻,学习了
深入分析Java的序列化与反序列化
理解Java对象序列化
Android序列化之Serializable和Parcelable