測試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的序列化、反序列化順序與字段聲明順序無關。