kryo

 
测试kryo与jdk的ObjectOutputStream

 

 

kryo常用设置

InstantiatorStrategy即初始化策略,默认kryo在反序列化对象时需要对象的类有一个零参数构造器,该构造器可以是private的,kryo通过反射调用该构造器来实例化对象。如果没有这样一个构造器,就需要使用kryo.setInstantiatorStrategy(new StdInstantiatorStrategy())了,该策略通过jvm api创建对象,这会创建一个完全空的对象(即不执行任何代码中的初始化工作),如果对象在实例化时需要一些初始化操作(比如在构造代码块中执行一些计算逻辑),这种策略就不可行了。
比较好的策略是kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));,即先尝试通过零参数构造实例化对象,如果类中没有零参数构造器,则会使用StdInstantiatorStrategy策略。
 
References即引用,对A对象序列化时,默认情况下kryo会在每个成员对象第一次序列化时写入一个数字,该数字逻辑上就代表了对该成员对象的引用,如果后续有引用指向该成员对象,则直接序列化之前存入的数字即可,而不需要再次序列化对象本身。这种默认策略对于成员存在互相引用的情况较有利,否则就会造成空间浪费(因为没序列化一个成员对象,都多序列化一个数字),通常情况下可以将该策略关闭,kryo.setReferences(false);
 
Registration即注册,kryo在序列化对象时,首先会序列化其类的全限定名,由于我们通常序列化的对象都是有限范围内的类的实例,这样重复序列化同样的类的全限定名是低效的。通过注册kryo可以将类的全限定名抽象为一个数字,即用一个数字代表全限定名,这样就要高效一些。kryo.register(SomeClass.class);,注册方法的完整签名为public Registration register (Class type, Serializer serializer, int id),我们通常只需要使用其重载方法即可public Registration register (Class type),serializer和id在kryo内部会指定。
 
PS:使用kryo序列化时,可以使用transient关键字忽略某字段。
 

序列化方法

kryo大体有三种序列化方法,每种方式都有其优势和劣势。
1,kryo.writeObject,这种方法只会序列化对象实例,而不会记录对象所属类的任何信息。优势是最节省空间,劣势是在反序列化时,需要提供类作为模板才能顺利反序列。反序列化时使用readObject。
2,直接kryo.writeClassAndObject,这种方法会先将对象所属类的全限定名序列化,然后再依次序列化对象实例的成员。优势是完全动态序列化,整个kryo周期都不需要提供类信息。反序列化时使用readClassAndObject
3,先注册,再kryo.writeClassAndObject,这种方式时最理想的,其结合了前两种优势,又有效规避了劣势,事先将需要序列化的类注册给kryo(此时类和唯一id绑定),之后使用writeClassAndObject序列化时,只会序列化注册id,而不会序列化类的全限定名了,这样大大节省了空间(通常只比writeObject多一个字节)。反序列化时使用readClassAndObject。注意序列化和反序列的kryo的注册信息应当保持一致。
 

Example

 1 public class Simple {  
 2  private String name;  
 3  private int age;  
 4  
 5  
 6  public String getName() {  
 7    return name;  
 8  }  
 9  
10   public void setName(String name) {  
11     this.name = name;  
12   }  
13   
14   public int getAge() {  
15     return age;  
16   }  
17   
18   public void setAge(int age) {  
19     this.age = age;  
20   }  
21   
22   static Simple getSimple() {  
23     Simple simple = new Simple();  
24     simple.setAge(10);  
25     simple.setName("zhang3");  
26     return simple;  
27   }  
28 }

 

1 Kryo kryo = new Kryo();  
2 kryo.setReferences(false);  
3 kryo.setRegistrationRequired(false);  
4 kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());  
5 Output output = new Output(new FileOutputStream("file.bin"));
6 kryo.writeClassAndObject(output, Simple.getSimple());  
7 output.close();

 

 

红色框内,第一个字节代表classId,也就是kryo中注册的id,01表示未注册,第二个字节代表nameId;

绿色框内为类的全限定名;

紫色框内,唯一的一个字节14表示age属性值10(cryo为了优化存储,定义了自己的映射关系);

粉色框内表示字符串“zhang3”,其中字符串“zhang”可以与asc码一一对应,最后一个字符“3”这里为B3就有些匪夷所思了,与14表示10类似,kryo对字符串也做了优化,这样可以省去表示长度的字节序列(具体优化策略就不探究了)。

如果序列化时,Sample中字段的顺序是age、name,反序列化时,Sample中字段的顺序是name、age,这样会不会有问题呢?

不会有问题,kryo在序列化、反序列前对字段进行了排序,kryo的序列化、反序列化顺序与字段声明顺序无关。

posted @ 2017-08-23 23:45  holoyong  阅读(2580)  评论(0编辑  收藏  举报