很多时候我们需要将一个类的实例变成二进制数据存储或是通过网络发送,这个过程叫序列化。如果将二进制数据解析成位于内存中的类实例或是相关数据结构,那叫反序列化。所有的序列化算法都遵循一定的套路,例如:
class A {
public int a = 1;
public int b = 2;
protected B b = new B();
private float c = 3.0;
}
如果要序列化类A的实例,那么通常需要将变量a,b的数值对应的二进制数写入,然后获得类B实例序列化后的二进制数据,最后将变量c的数值的二进制数据,这里可以体会到,序列化其实有一种递归的性质,在序列化过程中如果遇到的是基础类型,那么可以直接获取其对应的二进制数据,如果遇到类实例,那么需要先序列化它,取得对应二进制数据。
而序列化过程中需要你了解对应类的定义,但如果我们不知道要序列化的对象,例如我们看不到类A的定义,我们只拿到了A对应的一个实例对象,那此时怎么序列化呢。这就需要用到java语言的反射特性,java编译器在编译类A时,不仅仅将它为它的各个字段分配了内存,而且还为类A的相关信息进行了设置和存储,例如A里面有多少字段,字段的类型是int, float, stirng,还是特定类对象,这些信息都一并设置并存储了起来,只要我们使用java反射提供的API就能获得这些信息,从而就能对任意类实现序列化。
因此序列化的万能套路是:
1,获得要序列化的类实例;
2,获得类中各个字段的属性,类型等相关信息。
3,如果字段属于基础数据,那么获得其数值的二进制数据。
4,如果对应字段是一个类实例,那么先递归的序列化该实例
根据以上步骤,当我们需要序列化任意一个类实例时,首先通过getClass获得其对应的Class类实例,然后调用getDeclaredFields()接口获得该实例所有的字段,其中包括public,protected,private,或者调用getFields()获得类实例声明或继承的公有字段。在序列化中,我们不能忘了序列化当前类实例的父类,因此可以调用getSuperClass()来获得当前实例的父类,这个过程会不断进行直到抵达根类为止。
每个字段都会对应一个元类叫Field,通过该类相关接口能获得字段的值。获取字段的数据首先需要确定字段的类型,如果是Boolean类型,那么可以调用Field类的getBoolean接口获得数据,如果是int类型,那么可以通过getInteger()接口获得数据,如果字段是类对象,那么就得递归的去获得其二进制数据,如果字段是基础类型,那么通过调用其getString()就能获得其数值的字符串形式。
在获取字段类型前,我们还需要知道字段的修饰属性,例如是public还是private,是不是static等,这些属性通过Field类的接口getModifier()获得,调用它会返回一个整形值,该值在相关比特位上设置1或0来表示修饰属性。在java语法中共有11中修饰属性,因此有11个比特位来对应,但我们不需要分析哪个比特位设置为1来获取字段属