在笔者以往对于面试者的考察中,就算是社招的同学,对于对象的序列化大多一知半解。
我:RPC调用的过程中,为什么要做序列化?
面试者:没有什么为什么,大家都是这么写代码的。(然后又说了一堆,但都没有回答问题)
我只能换一个问法:不做序列化,有没有问题?
面试者:????
先来看一下维基百科上对 "序列化"的定义:序列化是把数据结构和对象的状态转换为一种可以存储和传输的格式。在后续重构对象的过程中(可能是在不同的上下文下重构),相同的字节流,可以重新构建一个和原对象语义上完全等同的新对象。
所以序列化要解决的问题,是使得 对象可以 存储和传输。经过序列化的对象,在存储或传输以后,在另外一个上下文中,通过反序列化,也能正确识别这个对象。常见的序列化的格式有PB,Json, xml等。
对象->序列化成指定格式->存储/传输->接收方反序列化->恢复对象,如果把序列化和反序列化去掉,就变成 对象->存储/传输->恢复对象
为了表达的更清楚,我们定义对象Customer
class Customer{ //客户
private int customerAge; //客户年龄
private String customerName; //客户姓名
}
问题1:服务器s1上java产生的Customer对象,如何传给服务器s2上的C++程序处理。
对象中的方法在内存中放在代码段,一个类下一个方法,无论有多少个对象产生,在内存中都只产生一段代码。而对象的数据存放在堆内存中,每个对象都有自己的一块内存用来存放自己的数据。
![](https://img.haomeiwen.com/i14912712/23f224a29aeb6157.png)
Java和C++的对象在内存中的布局规则是不一样,导致对于同样一块内存数据,两个语言对它有不同的识别。
问题2:那么s2上的环境,如果和s1的完全一样,是不是不做序列化,也能互相识别对象的数据呢?这里的环境完全一样,包括都是java程序,CPU,内存这些和s1完全一样。
答案是:是的。
s1上对象在内存中的数据,看成一个buffer, 逐个字节通过网路传输到s2,s2把这个buffer拷贝到堆中,当作一个对象的数据。由于S1和S2完全相同,那么S2上的代码段也能正确识别这块内存数据。
因为对象从S1到S2上,只是对象在堆中的地址变了。但是地址变了并不影响对对象的识别。
现在我们把对象的结构改一改再看。
class Customer{ //客户
private int customerAge; //客户年龄
private String customerName; //客户姓名
private Habit customerHabit; //客户爱好
}
class Habit{ //爱好
String description; //爱好描述
}
对象Customer中引用了另外一个对象Habit.如果不使用序列化,那么Customer中Habit记录的是S1上内存的地址。在S2上这个地址肯定是和S1上不一样的,S1还是会按照这个地址去访问Habit。
使用了序列化以后,就不会有这个问题。我们来看一下序列化是怎么解决这个问题的。
在S1上序列化User时,发现Customer引用了Habit,因此会先序列化Habit, 然后再序列化Customer, 设置好对Habit的引用。数据传输到s2后,反序列化会将Habit先进行反序列化,放到内存中,然后再反序列化Customer, 并正确设置对Habit的引用。
网友评论