深入挖掘 FST 快速序列化壓縮內(nèi)存的利器的特性和原理

FST 的概念和定義

FST 序列化全稱是 Fast Serialization Tool致份,它是對 Java 序列化的替換實現(xiàn)逻族。既然前文中提到 Java 序列化的兩點嚴重不足,在 FST 中得到了較大的改善束昵,F(xiàn)ST 的特征如下:

  • JDK 提供的序列化提升了 10 倍拔稳,體積也減少 3-4 倍多

  • 支持堆外 Maps,和堆外 Maps 的持久化

  • 支持序列化為 JSON

FST 序列化的使用

FST 的使用有兩種方式锹雏,一種是快捷方式巴比,另一種需要使用 ObjectOutput 和 ObjectInput。

直接使用 FSTConfiguration 提供的序列化和反序列化接口

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n19" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public static void serialSample() {
 FSTConfiguration conf = FSTConfiguration.createAndroidDefaultConfiguration();
 User object = new User();
 object.setName("huaijin");
 object.setAge(30);
 System.out.println("serialization, " + object);
 byte[] bytes = conf.asByteArray(object);
 User newObject = (User) conf.asObject(bytes);
 System.out.println("deSerialization, " + newObject);
}</pre>

FSTConfiguration 也提供了注冊對象的 Class 接口礁遵,如果不注冊轻绞,默認會將對象的 Class Name 寫入。這個提供了易用高效的 API 方式佣耐,不使用 ByteArrayOutputStreams 而直接得到 byte[]政勃。

使用 ObjectOutput 和 ObjectInput,能更細膩控制序列化的寫入寫出:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n24" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">static FSTConfiguration conf = FSTConfiguration.createAndroidDefaultConfiguration();
static void writeObject(OutputStream outputStream, User user) throws IOException {
 FSTObjectOutput out = conf.getObjectOutput(outputStream);        out.writeObject(user);
 out.close();}
static FstObject readObject(InputStream inputStream) throws Exception {    FSTObjectInput input = conf.getObjectInput(inputStream);
 User fstObject = (User) input.readObject(User.class);                          input.close();
 return fstObject;}</pre>

FST 在 Dubbo 中的應(yīng)用

  • Dubbo 中對 FstObjectInputFstObjectOutput 重新包裝解決了序列化和反序列化空指針的問題兼砖。

  • 并且構(gòu)造了 FstFactory 工廠類奸远,使用工廠模式生成 FstObjectInput 和 FstObjectOutput既棺。其中同時使用單例模式,控制整個應(yīng)用中 FstConfiguration 是單例懒叛,并且在初始化時將需要序列化的對象全部注冊到 FstConfiguration丸冕。

  • 對外提供了同一的序列化接口 FstSerialization,提供 serialize 和 deserialize 能力薛窥。

FST 序列化/反序列化

FST 序列化存儲格式

基本上所有以 Byte 形式存儲的序列化對象都是類似的存儲結(jié)構(gòu)胖烛,不管 class 文件、so 文件拆檬、dex 文件都是類似洪己,這方面沒有什么創(chuàng)新的格式妥凳,最多是在字段內(nèi)容上做了一些壓縮優(yōu)化竟贯,包括我們最常使用的 utf-8 編碼都是這個做法。

FST 的序列化存儲和一般的字節(jié)格式化存儲方案也沒有標新立異的地方逝钥,比如下面這個 FTS 的序列化字節(jié)文件

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n41" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">00000001:  0001 0f63 6f6d 2e66 7374 2e46 5354 4265
00000010:  616e f701 fc05 7630 7374 7200 </pre>

格式:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n44" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

Header|類名長度|類名String|字段1類型(1Byte) | [長度] | 內(nèi)容|字段2類型(1Byte) | [長度] | 內(nèi)容|…</pre>

  • 0000:字節(jié)數(shù)組類型:00 標識 OBJECT

  • 0001:類名編碼屑那,00 標識 UTF 編碼,01 表示 ASCII 編碼

  • 0002:Length of class name (1Byte) = 15

  • 0003~0011:Class name string (15Byte)

  • 0012:Integer 類型標識 0xf7

  • 0013:Integer 的值=1

  • 0014:String 類型標識 0xfc

  • 0015:String 的長度=5

  • 0016~001a:String 的值"v0str"

  • 001b~001c:END

從上面可以看到 Integer 類型序列化后只占用了一個字節(jié)(值等于 1)艘款,并不像在內(nèi)存中占用 4Byte持际,所以可以看出是根據(jù)一定規(guī)則做了壓縮,具體代碼看FSTObjectInput#instantiateSpecialTag中對不同類型的讀取哗咆,F(xiàn)STObjectInput 也定義不同類型對應(yīng)的枚舉值:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n71" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class FSTObjectOutput implements ObjectOutput {
 private static final FSTLogger LOGGER = FSTLogger.getLogger(FSTObjectOutput.class);
 public static Object NULL_PLACEHOLDER = new Object() {
 public String toString() { return "NULL_PLACEHOLDER"; }};
 public static final byte SPECIAL_COMPATIBILITY_OBJECT_TAG = -19; // see issue 52
 public static final byte ONE_OF = -18;
 public static final byte BIG_BOOLEAN_FALSE = -17;
 public static final byte BIG_BOOLEAN_TRUE = -16;
 public static final byte BIG_LONG = -10; 
 public static final byte BIG_INT = -9;
 public static final byte DIRECT_ARRAY_OBJECT = -8;
 public static final byte HANDLE = -7;
 public static final byte ENUM = -6;
 public static final byte ARRAY = -5;
 public static final byte STRING = -4;
 public static final byte TYPED = -3; // var class == object written class
 public static final byte DIRECT_OBJECT = -2;
 public static final byte NULL = -1;
 public static final byte OBJECT = 0;
 protected FSTEncoder codec;
 ...
}</pre>

FST 序列化和反序列化原理

對 Object 進行 Byte 序列化蜘欲,相當于做了持久化的存儲,在反序列的時候晌柬,如果 Bean 的定義發(fā)生了改變姥份,那么反序列化器就要做兼容的解決方案,我們知道對于 JDK 的序列化和反序列年碘,serialVersionUID 對版本控制起了很重要的作用澈歉。FST 對這個問題的解決方案是通過 @Version 注解進行排序。

在進行反序列操作的時候屿衅,F(xiàn)ST 會先反射或者對象 Class 的所有成員埃难,并對這些成員進行了排序,這個排序?qū)嫒萜鹆岁P(guān)鍵作用涤久,也就是 @Version 的原理涡尘。在 FSTClazzInfo 中定義了一個 defFieldComparator 比較器,用于對 Bean 的所有 Field 進行排序:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n78" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public final class FSTClazzInfo {
 public static final Comparator<FSTFieldInfo> defFieldComparator = new Comparator<FSTFieldInfo>() {
 @Override
 public int compare(FSTFieldInfo o1, FSTFieldInfo o2) {
 int res = 0;

 if ( o1.getVersion() != o2.getVersion() ) {
 return o1.getVersion() < o2.getVersion() ? -1 : 1;
 }
 // order: version, boolean, primitives, conditionals, object references 
 if (o1.getType() == boolean.class && o2.getType() != boolean.class) {
 return -1;
 } 
 if (o1.getType() != boolean.class && o2.getType() == boolean.class) {
 return 1; 
 }
 if (o1.isConditional() && !o2.isConditional()) { 
 res = 1; 
 } else if (!o1.isConditional() && o2.isConditional()) {                res = -1;
 } else if (o1.isPrimitive() && !o2.isPrimitive()) {                                 res = -1;
 } else if (!o1.isPrimitive() && o2.isPrimitive())                                   res = 1;
//              if (res == 0) // 64 bit / 32 bit issues
//                  res = (int) (o1.getMemOffset() - o2.getMemOffset());            
 if (res == 0)
 res = o1.getType().getSimpleName().compareTo(o2.getType().getSimpleName());            if (res == 0)
 res = o1.getName().compareTo(o2.getName());
 if (res == 0) {    return o1.getField().getDeclaringClass().getName().compareTo(o2.getField().getDeclaringClass().getName()); 
 } 
 return res;
 } 
 };
 ...
}</pre>

從代碼實現(xiàn)上可以看到响迂,比較的優(yōu)先級是 Field 的 Version 大小谍椅,然后是 Field 類型怠蹂,所以總的來說 Version 越大排序越靠后,至于為什么要排序谨湘,看下 FSTObjectInput#instantiateAndReadNoSer 方法

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n83" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class FSTObjectInput implements ObjectInput {
 protected Object instantiateAndReadNoSer(Class c, FSTClazzInfo clzSerInfo, FSTClazzInfo.FSTFieldInfo referencee, int readPos) throws Exception {                   Object newObj;                                                                 newObj = clzSerInfo.newInstance(getCodec().isMapBased());
 ...
 } else {
 FSTClazzInfo.FSTFieldInfo[] fieldInfo = clzSerInfo.getFieldInfo();             readObjectFields(referencee, clzSerInfo, fieldInfo, newObj,0,0);           }
 return newObj;
 }
 protected void readObjectFields(FSTClazzInfo.FSTFieldInfo referencee, FSTClazzInfo serializationInfo, FSTClazzInfo.FSTFieldInfo[] fieldInfo, Object newObj, int startIndex, int version) throws Exception {
 if ( getCodec().isMapBased() ) {
 readFieldsMapBased(referencee, serializationInfo, newObj);                     if ( version >= 0 && newObj instanceof Unknown == false)                           getCodec().readObjectEnd();
 return;
 }
 if ( version < 0 )
 version = 0;
 int booleanMask = 0;
 int boolcount = 8;
 final int length = fieldInfo.length;
 int conditional = 0;
 for (int i = startIndex; i < length; i++) {  // 注意這里的循環(huán)
 try {
 FSTClazzInfo.FSTFieldInfo subInfo = fieldInfo[i];                            if (subInfo.getVersion() > version ) {   // 需要進入下一個版本的迭代 
 int nextVersion = getCodec().readVersionTag();  // 對象流的下一個版本 
 if ( nextVersion == 0 ) // old object read
 {
 oldVersionRead(newObj); 
 return; 
 } 
 if ( nextVersion != subInfo.getVersion() ) {  // 同一個Field的版本不允許變,并且版本變更和流的版本保持同步 
 throw new RuntimeException("read version tag "+nextVersion+" fieldInfo has "+subInfo.getVersion()); 
 }          readObjectFields(referencee,serializationInfo,fieldInfo,newObj,i,nextVersion);  // 開始下一個Version的遞歸 
 return;
 } 
 if (subInfo.isPrimitive()) {
 ...
 } else {
 if ( subInfo.isConditional() ) { 
 ... 
 } // object 把讀出來的值保存到FSTFieldInfo中                    Object subObject = readObjectWithHeader(subInfo);                    subInfo.setObjectValue(newObj, subObject);
 } 
 ...</pre>

從這段代碼的邏輯基本就可以知道 FST 的序列化和反序列化兼容的原理了骂际,注意里面的循環(huán),正是按照排序后的 Filed 進行循環(huán),而每個 FSTFieldInfo 都記錄自己在對象流中的位置镇防、類型等詳細信息:

序列化:

  • 按照 Version 對 Bean 的所有 Field 進行排序(不包括 static 和 transient 修飾的 member),沒有 @Version 注解的 Field 默認 version=0潮饱;如果 version 相同来氧,按照 version, boolean, primitives, conditionals, object references 排序

  • 按照排序的 Field 把 Bean 的 Field 逐個寫到輸出流

  • @Version 的版本只能加不能減小,如果相等的話香拉,有可能因為默認的排序規(guī)則啦扬,導(dǎo)致流中的 Filed 順序和內(nèi)存中的 FSTFieldInfo[]數(shù)組的順序不一致,而注入錯誤

反序列化:

  • 反序列化按照對象流的格式進行解析凫碌,對象流中保存的 Field 順序和內(nèi)存中的 FSTFieldInfo 順序保持一致

  • 相同版本的 Field 在對象流中存在扑毡,在內(nèi)存 Bean 中缺失:可能拋異常(會有后向兼容問題)

  • 對象流中包含內(nèi)存 Bean 中沒有的高版本 Field:正常(老版本兼容新)

  • 相同版本的 Field 在對象流中缺失,在內(nèi)存 Bean 中存在:拋出異常

  • 相同的 Field 在對象流和內(nèi)存 Bean 中的版本不一致:拋出異常

  • 內(nèi)存 Bean 增加了不高于最大版本的 Field:拋出異常

所以從上面的代碼邏輯就可以分析出這個使用規(guī)則:@Version 的使用原則就是盛险,每新增一個 Field瞄摊,就對應(yīng)的加上 @Version 注解,并且把 version 的值設(shè)置為當前版本的最大值加一苦掘,不允許刪除 Field

另外再看一下 @Version 注解的注釋:明確說明了用于后向兼容

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n114" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">package org.nustaq.serialization.annotations;
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})

/**
* support for adding fields without breaking compatibility to old streams. 
* For each release of your app increment the version value. No Version annotation means version=0.
* Note that each added field needs to be annotated.
*
* e.g.
*
* class MyClass implements Serializable {
*
*     // fields on initial release 1.0 
*     int x;
*     String y;
*
*     // fields added with release 1.5
*     @Version(1) String added;
*     @Version(1) String alsoAdded;
*
*     // fields added with release 2.0
*     @Version(2) String addedv2;
*     @Version(2) String alsoAddedv2;
*
* }
*
* If an old class is read, new fields will be set to default values. You can register a VersionConflictListener
* at FSTObjectInput in order to fill in defaults for new fields.
*
* Notes/Limits:
* - Removing fields will break backward compatibility. You can only Add new fields.
* - Can slow down serialization over time (if many versions)
* - does not work for Externalizable or Classes which make use of JDK-special features such as readObject/writeObject
*   (AKA does not work if fst has to fall back to 'compatible mode' for an object).
* - in case you use custom serializers, your custom serializer has to handle versioning
*
*/public @interface Version {
 byte value();
}</pre>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n117" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class FSTBean implements Serializable {
 /** serialVersionUID */
 private static final long serialVersionUID = -2708653783151699375L;             private Integer v0in
 private String v0str;
}</pre>

準備序列化和反序列化方法

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n120" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class FSTSerial {

 private static void serialize(FstSerializer fst, String fileName) {                 try {
 FSTBean fstBean = new FSTBean();
 fstBean.setV0int(1);
 fstBean.setV0str("v0str");
 byte[] v1 = fst.serialize(fstBean);

 FileOutputStream fos = new FileOutputStream(new File("byte.bin"));             fos.write(v1, 0, v1.length);
 fos.close();

 } catch (Exception e) {
 e.printStackTrace();
 }
 }

 private static void deserilize(FstSerializer fst, String fileName) {           try {
 FileInputStream fis = new FileInputStream(new File("byte.bin"));               ByteArrayOutputStream baos = new ByteArrayOutputStream();                       byte[] buf = new byte[256];
 int length = 0;
 while ((length = fis.read(buf)) > 0) {
 baos.write(buf, 0, length);
 }
 fis.close();
 buf = baos.toByteArray();
 FSTBean deserial = fst.deserialize(buf, FSTBean.class);                         System.out.println(deserial);
 System.out.println(deserial);

 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 public static void main(String[] args) {
 FstSerializer fst = new FstSerializer();
 serialize(fst, "byte.bin");
 deserilize(fst, "byte.bin");
 }
}</pre>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末换帜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鹤啡,更是在濱河造成了極大的恐慌惯驼,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件递瑰,死亡現(xiàn)場離奇詭異祟牲,居然都是意外死亡,警方通過查閱死者的電腦和手機泣矛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門疲眷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人您朽,你說我怎么就攤上這事狂丝。” “怎么了哗总?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵几颜,是天一觀的道長。 經(jīng)常有香客問我讯屈,道長蛋哭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任涮母,我火速辦了婚禮谆趾,結(jié)果婚禮上躁愿,老公的妹妹穿的比我還像新娘。我一直安慰自己沪蓬,他們只是感情好彤钟,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跷叉,像睡著了一般逸雹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上云挟,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天梆砸,我揣著相機與錄音,去河邊找鬼园欣。 笑死帖世,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的俊庇。 我是一名探鬼主播狮暑,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辉饱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拣展,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤彭沼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后备埃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姓惑,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年按脚,在試婚紗的時候發(fā)現(xiàn)自己被綠了于毙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡辅搬,死狀恐怖唯沮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堪遂,我是刑警寧澤介蛉,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站溶褪,受9級特大地震影響币旧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猿妈,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一吹菱、第九天 我趴在偏房一處隱蔽的房頂上張望巍虫。 院中可真熱鬧,春花似錦鳍刷、人聲如沸垫言。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筷频。三九已至,卻和暖如春前痘,著一層夾襖步出監(jiān)牢的瞬間凛捏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工芹缔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坯癣,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓最欠,卻偏偏與公主長得像示罗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子芝硬,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容