Java序列化與反序列化

什么是序列化與反序列化

序列化是指把對(duì)象轉(zhuǎn)換為字節(jié)序列的過程(Encoding an object as a byte stream is known as serializing the object.)

反序列化是指把字節(jié)序列恢復(fù)為對(duì)象的過程(The reverse process is known as deserializing it.)

序列化與反序列化面向的是成員變量,類的方法和靜態(tài)變量不參與序列化與反序列化楷扬。

什么場(chǎng)景下需要用到序列化與反序列化

當(dāng)兩個(gè)進(jìn)程進(jìn)行遠(yuǎn)程通信時(shí)慕趴,可以相互發(fā)送各種類型的數(shù)據(jù)绰姻,包括文本、圖片铭乾、音頻碰凶、視頻等分瘾, 而這些數(shù)據(jù)都會(huì)以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。那么當(dāng)兩個(gè)Java進(jìn)程進(jìn)行通信時(shí)紊婉,能否實(shí)現(xiàn)進(jìn)程間的對(duì)象傳送呢药版?答案是可以的。如何做到呢喻犁?這就需要Java序列化與反序列化了槽片。換句話說,一方面株汉,發(fā)送方需要把這個(gè)Java對(duì)象轉(zhuǎn)換為字節(jié)序列筐乳,然后在網(wǎng)絡(luò)上傳送;另一方面乔妈,接收方需要從字節(jié)序列中恢復(fù)出Java對(duì)象蝙云。

當(dāng)我們明晰了為什么需要Java序列化和反序列化后,我們很自然地會(huì)想Java序列化的好處路召。其好處一是實(shí)現(xiàn)了數(shù)據(jù)的持久化勃刨,通過序列化可以把數(shù)據(jù)永久地保存到硬盤上(通常存放在文件里),二是股淡,利用序列化實(shí)現(xiàn)遠(yuǎn)程通信身隐,即在網(wǎng)絡(luò)上傳送對(duì)象的字節(jié)序列。

即:涉及I/O(磁盤I/O 網(wǎng)絡(luò)I/O)唯灵,需要用到序列化與反序列化贾铝。

如何實(shí)現(xiàn)序列化與反序列化
以JDK實(shí)現(xiàn)方式為例,參考JDK中與I/O相關(guān)的類。

序列化

I/O OutputStream:

OutputStream Family

在序列化過程中垢揩,主要使用DataOutputStream(primitive Java data types) / ObjectOutputStream(primitive Java data types & objects) / OutputStream玖绿。

// serialize
public static <T> byte[] serialize(T obj) {
    ObjectOutputStream objectOutputStream = null;
    ByteArrayOutputStream byteArrayOutputStream = null;
    try {
        byteArrayOutputStream = new ByteArrayOutputStream();
        objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(obj);
        return byteArrayOutputStream.toByteArray();
    } catch (Exception e) {
        logger.error("serialize error", e);
    }
    return null;
}

反序列化

I/O InputStream:

InputStream Family

在反序列化過程中,主要使用DataInputStream(read primitive Java data types) / ObjectInputStream(read primitive data and objects) / InputStream叁巨。

// deserialize
@SuppressWarnings("unchecked")
public static <T> T deserialize(byte[] bytes) {
    try {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(byteArrayInputStream);
        return (T) ois.readObject();
    } catch (Exception e) {
        logger.error("deserialize error", e);
    }
    return null;
}

transient

使用transient關(guān)鍵字修飾的實(shí)例變量不會(huì)參與默認(rèn)的序列化及反序列化斑匪,如實(shí)例的敏感信息字段等:

public class Person implements java.io.Serializable {
  ...
  private transient String phone;
  ...
}

這里提到了默認(rèn)的序列化及反序列化,如果使用自定義序列化方式锋勺,則可以對(duì)transient關(guān)鍵字修飾的實(shí)例變量進(jìn)行序列化及反序列化蚀瘸,以實(shí)現(xiàn)Externalizable接口自定義序列化、反序列化為例:

public class ExternalizablePerson implements java.io.Externalizable {
    private static final long serialVersionUID = 1866740372404660450L;

    private String name;
    private Integer age;
    private String career;
    private transient String phone;

    /**
     * 自定義序列化方式
     * @param out
     * @throws IOException
     */
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
        out.writeInt(age);
        out.writeUTF(career);
        out.writeUTF(phone);
    }

    /**
     * 自定義反序列化方式
     * @param in
     * @throws IOException
     * @throws ClassNotFoundException
     */
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = in.readUTF();
        age = in.readInt();
        career = in.readUTF();
        phone = in.readUTF();
    }
}

關(guān)于Externalizable接口庶橱,可以參考:What is the difference between Serializable and Externalizable in Java

serialVersionUID

serialVersionUID的取值是Java運(yùn)行時(shí)環(huán)境根據(jù)類的內(nèi)部細(xì)節(jié)自動(dòng)生成的贮勃。如果對(duì)類的源代碼作了修改,再重新編譯悬包,新生成的類文件的serialVersionUID的取值有可能會(huì)發(fā)生變化衙猪。
類的serialVersionUID的默認(rèn)值完全依賴于Java編譯器的實(shí)現(xiàn),對(duì)于同一個(gè)類布近,用不同的Java編譯器編譯垫释,有可能會(huì)導(dǎo)致不同的serialVersionUID。為了提高serialVersionUID的獨(dú)立性和確定性撑瞧,強(qiáng)烈建議在一個(gè)可序列化類中顯示的定義serialVersionUID棵譬,為它賦予明確的值。

關(guān)于serialVersionUID预伺,可以參考:What is a serialVersionUID and why should I use it?

writeReplace()/writeObject()/readObject()/readResolve()

這四個(gè)方法在 java.io.ObjectStreamClass 中定義订咸,用于自定義序列化、反序列化酬诀。如果在Java實(shí)體類中聲明了這些方法脏嚷,執(zhí)行順序如下(以反射的方式執(zhí)行,如writeObject在ObjectOutputStream中被調(diào)用瞒御,readObject在ObjectInputStream中被調(diào)用):

執(zhí)行順序 方法名稱 類型 作用 方法描述
1 writeReplace 序列化 在writeObject之前執(zhí)行父叙,可用于替換將要被序列化的實(shí)例(序列化代理) private Object writeReplace() {}
2 writeObject 序列化 對(duì)ObjectOutputStream中的byte執(zhí)行序列化操作 private void writeObject(java.io.ObjectOutputStream out) throws IOException {}
3 readObject 反序列化 對(duì)ObjectInputStream中的byte執(zhí)行反序列化操作 private void readObject(java.io.ObjectInputStream out) throws IOException, ClassNotFoundException {}
4 readResolve 反序列化 在readObject之后執(zhí)行,可用于替換返回的反序列化實(shí)例(單例) private Object readResolve() {}

序列化與反序列化中的注意事項(xiàng)

謹(jǐn)慎實(shí)現(xiàn)java.io.Serializable接口

實(shí)現(xiàn)java.io.Serializable接口后肴裙,會(huì)造成以下影響:

  • 一旦類被發(fā)布趾唱,會(huì)大大降低修改該發(fā)布類的靈活性

如果一個(gè)類實(shí)現(xiàn)了Serializable接口,它的字節(jié)流編碼也變成了它導(dǎo)出API的一部分蜻懦,它的子類都等價(jià)于實(shí)現(xiàn)了序列化甜癞,以后如果想要改變這個(gè)類的內(nèi)部表示法(添加/修改/刪除成員變量等),可能導(dǎo)致序列化形式不兼容宛乃。

  • 增加了出現(xiàn)Bug和安全漏洞的可能性

一般對(duì)象是由構(gòu)造器創(chuàng)建的悠咱,而序列化也是一種對(duì)象創(chuàng)建機(jī)制蒸辆,反序列化也可以構(gòu)造對(duì)象,默認(rèn)的反序列化機(jī)制構(gòu)造對(duì)象過程中乔煞,很容易遭到非法訪問吁朦,使構(gòu)造出來的對(duì)象柒室,并不是原始對(duì)象渡贾,引發(fā)程序Bug和其他安全問題。

  • 隨著類的新版本發(fā)布雄右,帶來了更大的測(cè)試成本

  • 性能開銷變大

序列化對(duì)象時(shí)空骚,不僅會(huì)序列化當(dāng)前對(duì)象本身,還會(huì)對(duì)該對(duì)象引用的其他對(duì)象也進(jìn)行序列化擂仍,從而增大系統(tǒng)開銷

保護(hù)性編寫readObject方法

反序列化可以繞過構(gòu)造函數(shù)生成實(shí)例囤屹,如果實(shí)例在構(gòu)造過程中存在業(yè)務(wù)上或邏輯上的限制,在序列化上使用了默認(rèn)的序列化方式(即只繼承java.io.Serializable接口)逢渔,則可以通過反序列化方式繞過構(gòu)造實(shí)例限制肋坚,從而生成不合法的實(shí)例。

以Peroid類為例肃廓,該類含有兩個(gè)字段:start(起始時(shí)間)智厌,end(終止時(shí)間),業(yè)務(wù)規(guī)則限制初始化Period實(shí)例時(shí)盲赊,start < end铣鹏,構(gòu)造函數(shù)如下:

// all args constructor
public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());
    // 檢查起止時(shí)間
    if (this.start.compareTo(this.end) > 0) {
        throw new IllegalArgumentException(start + " after " + end);
    }
}

現(xiàn)在通過以下byte[],反序列化生成Period實(shí)例:

private static final byte[] serializedBytes = new byte[] {
    (byte)0xac, (byte)0xed, 0x00, 0x05, 0x73, 0x72, 0x00, 0x10, 0x64, 0x65, 0x66, 0x65, 0x6e, 0x73, 0x69, 0x76,
    0x65, 0x2e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x39, (byte)0xc0, (byte)0x85, 0x2d, 0x4a, (byte)0xc1, 0x6b,
    0x44, 0x02, 0x00, 0x02, 0x4c, 0x00, 0x03, 0x65, 0x6e, 0x64, 0x74, 0x00, 0x10, 0x4c, 0x6a, 0x61, 0x76, 0x61,
    0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x44, 0x61, 0x74, 0x65, 0x3b, 0x4c, 0x00, 0x05, 0x73, 0x74, 0x61, 0x72,
    0x74, 0x71, 0x00, 0x7e, 0x00, 0x01, 0x78, 0x70, 0x73, 0x72, 0x00, 0x0e, 0x6a, 0x61, 0x76, 0x61, 0x2e, 0x75,
    0x74, 0x69, 0x6c, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x68, 0x6a, (byte)0x81, 0x01, 0x4b, 0x59, 0x74, 0x19, 0x03,
    0x00, 0x00, 0x78, 0x70, 0x77, 0x08, 0x00, 0x00, 0x01, 0x63, 0x79, 0x21, 0x10, 0x00, 0x78, 0x73, 0x71, 0x00,
    0x7e, 0x00, 0x03, 0x77, 0x08, 0x00, 0x00, 0x01, 0x63, (byte)0xb1, (byte)0xc7, 0x04, 0x00, 0x78
};

public static void main(String[] args) throws Exception {
    // create instance via deserialize
    Period illegalPeriod = SerializeUtil.deserialize(serializedBytes);
    log.info("Period instance from deserialize: {}", illegalPeriod);
}

通過這種方式哀蘑,我們構(gòu)造出了實(shí)例屬性start == 2018-05-31 00:00:00诚卸,end == 2018-05-20 00:00:00的非法實(shí)例。為了解決這一問題绘迁,需要通過編寫readObject方法合溺,禁止通過反序列化創(chuàng)建非法實(shí)例:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    // check start < end
    if (start.compareTo(end) > 0) {
        throw new InvalidObjectException(start + " after " + end);
    }
}

查看完整代碼

使用序列化代理代替序列化實(shí)例

Serial Proxy

通過創(chuàng)建實(shí)例變量完全一致的靜態(tài)內(nèi)部類,作為序列化代理類缀台,將序列化棠赛、反序列化過程交由序列化代理類完成:

/**
 * 序列化代理類
 */
private static class PersonProxy implements Serializable {

    private static final long serialVersionUID = -2902051239103230395L;

    private String name;
    private Integer age;
    private String career;
    private String phone;

    public PersonProxy(Person p) {
        log.info("PersonProxy(Person original)");
        this.name = p.getName();
        this.age = p.getAge();
        this.career = p.getCareer();
        this.phone = p.getPhone();
    }

    private Object readResolve() {
        log.info("PersonProxy.readResolve()");
        Person person = new Person(name, age, career, phone);
        return person;
    }

    private void readObject(ObjectInputStream in) throws Exception {
        log.info("PersonProxy.readObject");
        in.defaultReadObject();
    }
}

private Object writeReplace() {
    log.info("Person.writeReplace()");
    return new PersonProxy(this);
}

/**
 * 本方法不會(huì)執(zhí)行,因?yàn)樾蛄谢呀?jīng)由PersonProxy實(shí)例代理
 * @param out
 */
private void writeObject(ObjectOutputStream out) {
    log.info("Person.writeObject()");
}

/**
 * 防止攻擊者偽造數(shù)據(jù)(age < 0 || age >> 100)
 * @param in
 * @return
 * @throws InvalidObjectException
 */
private void readObject(ObjectInputStream in) throws InvalidObjectException {
    throw new InvalidObjectException("Proxy required");
}

查看完整代碼

通過使用序列化代理類将硝,可以方便編寫保護(hù)性的readObject方法恭朗,避免因序列化、反序列化使用不當(dāng)造成潛在系統(tǒng)漏洞依疼。

序列化與反序列化可能造成的系統(tǒng)漏洞

如“保護(hù)性編寫readObject方法”所示痰腮,通過構(gòu)造bytes可以在反序列化時(shí)生成不合法的實(shí)例,從而導(dǎo)致系統(tǒng)漏洞律罢。

第三方序列化工具類介紹

Hessian

Hessian是一個(gè)輕量級(jí)的remoting on http工具膀值,使用簡單的方法提供了RMI的功能棍丐。 相比WebService,Hessian更簡單沧踏、快捷歌逢。采用的是二進(jìn)制RPC協(xié)議,因?yàn)椴捎玫氖嵌M(jìn)制協(xié)議翘狱,所以它很適合于發(fā)送二進(jìn)制數(shù)據(jù)秘案。

示例

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
...

Person person = new Person();
person.setName("Tom");
person.setAge(25);
person.setCareer("engineer");
person.setPhone("15201726287");

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// serialize
HessianOutput hessianOutput = new HessianOutput(byteArrayOutputStream);
hessianOutput.writeObject(person);
byte[] bytes = byteArrayOutputStream.toByteArray();
log.info("serialized result: {}", bytes);
// deserialize
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
HessianInput hessianInput = new HessianInput(byteArrayInputStream);
Person deserializedPerson = (Person) hessianInput.readObject();
log.info("deserialized result: {}", deserializedPerson);

應(yīng)用

Hessian

Protobuf

Protobuf是一種輕便高效的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式,用于結(jié)構(gòu)化數(shù)據(jù)序列化潦匈。它很適合做數(shù)據(jù)存儲(chǔ)或RPC數(shù)據(jù)交換格式阱高,可用于通訊協(xié)議、數(shù)據(jù)存儲(chǔ)等領(lǐng)域茬缩。Protobuf是一種跨語言赤惊、跨平臺(tái)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式凰锡。

示例

// Filename: person.proto

syntax="proto2";
option java_package = "protobuf.bean";

message Person {
    required string name = 1;
    required int32 age = 2;
    required string phone = 3;
    required string career = 4;
}
# generate Java class
protoc --java_out=[dir] [.proto file path]
PersonOuterClass.Person.Builder  personBuilder = PersonOuterClass.Person.newBuilder();
personBuilder.setName("Jerry");
personBuilder.setAge(20);
personBuilder.setCareer("teacher");
personBuilder.setPhone("15112933840");
PersonOuterClass.Person person = personBuilder.build();
log.info("build result: {}", person);
// serialize
byte[] bytes = person.toByteArray();
log.info("serialized result: {}", bytes);
// deserialize
PersonOuterClass.Person deserializePerson = PersonOuterClass.Person.parseFrom(bytes);
log.info("deserialized result: {}", deserializePerson);

應(yīng)用

gRPC

Kryo

Kryo是一個(gè)快速高效的Java序列化框架未舟,旨在提供快速、高效和易用的API掂为。無論文件裕膀、數(shù)據(jù)庫或網(wǎng)絡(luò)數(shù)據(jù)Kryo都可以隨時(shí)完成序列化。Kryo還可以執(zhí)行自動(dòng)深拷貝(克缕刑汀)魂角、淺拷貝(克隆)智绸。這是對(duì)象到對(duì)象的直接拷貝野揪,非對(duì)象->字節(jié)->對(duì)象的拷貝。

示例

序列化/反序列化:

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
...

Kryo kryo = new Kryo();
// declare Output stream
Output output = new Output(new FileOutputStream("file.bin"));
Person person = new Person();
person.setName("Tom");
person.setAge(25);
person.setCareer("engineer");
person.setPhone("15201726287");
// serialize
kryo.writeObject(output, person);
output.close();
// declare Input stream
Input input = new Input(new FileInputStream("file.bin"));
// deserialize
Person deserializePerson = kryo.readObject(input, Person.class);
log.info("deserialize result: {}", deserializePerson);
input.close();

深拷貝/淺拷貝:

Kryo kryo = new Kryo();
Engineer engineer = new Engineer();
engineer.setName("Tom");
engineer.setAge(25);
engineer.setCareer("engineer");
engineer.setPhone("15201726287");
// non primary type
List<String> skills = new ArrayList<>();
skills.add("math");
skills.add("programming");
skills.add("system");
skills.add("algorithm");
engineer.setSkills(skills);
// deep copy
Engineer deepCopyEngineer = kryo.copy(engineer);
log.info("deep copy result: {}, is skills equal: {}",
         deepCopyEngineer, engineer.getSkills() == deepCopyEngineer.getSkills());
// shallow copy
Engineer shallowCopyEngineer = kryo.copyShallow(engineer);
log.info("shallow copy result: {}, is skills equal: {}",
         shallowCopyEngineer, engineer.getSkills() == shallowCopyEngineer.getSkills());

應(yīng)用

Hive Spark Storm Akka

總結(jié)

關(guān)于序列化與反序列化的知識(shí)還有很多斯稳,如:

  1. java.util.HashMap迹恐、java.util.ArrayList 等集合類中關(guān)于序列化及反序列的自定義處理,為什么這么處理
  2. Kryo殴边、Protobuf等第三方序列化工具做了哪些工作,使得序列化锤岸、反序列化速度相比JDK自帶的序列化、反序列速度優(yōu)秀很多

等是偷,在后續(xù)系列博客中將繼續(xù)探索募逞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末馋评,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子留特,更是在濱河造成了極大的恐慌纠脾,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磕秤,死亡現(xiàn)場(chǎng)離奇詭異乳乌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)市咆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來再来,“玉大人蒙兰,你說我怎么就攤上這事∶⑴瘢” “怎么了搜变?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長针炉。 經(jīng)常有香客問我挠他,道長,這世上最難降的妖魔是什么篡帕? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任殖侵,我火速辦了婚禮,結(jié)果婚禮上镰烧,老公的妹妹穿的比我還像新娘拢军。我一直安慰自己,他們只是感情好怔鳖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布茉唉。 她就那樣靜靜地躺著,像睡著了一般结执。 火紅的嫁衣襯著肌膚如雪度陆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天献幔,我揣著相機(jī)與錄音懂傀,去河邊找鬼。 笑死斜姥,一個(gè)胖子當(dāng)著我的面吹牛鸿竖,可吹牛的內(nèi)容都是我干的沧竟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缚忧,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼悟泵!你這毒婦竟也來了闪水?” 一聲冷哼從身側(cè)響起球榆,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎衡招,沒想到半個(gè)月后始腾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體空执,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奶栖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年宣鄙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了框冀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敏簿。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡温数,死狀恐怖蜻势,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情够傍,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布寂诱,位于F島的核電站痰洒,受9級(jí)特大地震影響丘喻,放射性物質(zhì)發(fā)生泄漏念颈。R本人自食惡果不足惜舍肠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一翠语、第九天 我趴在偏房一處隱蔽的房頂上張望肌括。 院中可真熱鬧谍夭,春花似錦憨募、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽待笑。三九已至暮蹂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間仰泻,已是汗流浹背我纪。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國打工浅悉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汹碱。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓咳促,卻偏偏與公主長得像跪腹,于是被迫代替她去往敵國和親飞醉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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

  • 一、 序列化和反序列化概念 Serialization(序列化)是一種將對(duì)象以一連串的字節(jié)描述的過程逗栽;反序列化de...
    步積閱讀 1,441評(píng)論 0 10
  • 在Java中失暂,我們可以通過多種方式來創(chuàng)建對(duì)象,并且只要對(duì)象沒有被回收我們都可以復(fù)用該對(duì)象兵志。但是想罕,我們創(chuàng)建出來的這些...
    懶癌正患者閱讀 1,530評(píng)論 0 12
  • 原帖地址:原帖個(gè)人網(wǎng)站地址:個(gè)人網(wǎng)站簡書對(duì)markdown的支持太完美了,我竟然可以直接Ctrl C/V過來惭适。 定...
    ryderchan閱讀 3,799評(píng)論 1 9
  • 瞧癞志,我們認(rèn)真的小模樣框产! 甜甜老師和李老師帶我們一起講故事…… 原來這里有一條“貪吃的蛇”! 這個(gè)故事告訴我們...
    A梅_4076閱讀 1,030評(píng)論 0 0
  • “媽媽隔崎,媽媽,你跑慢點(diǎn)啊爵卒,我快追不上你了技潘∏Э担”小鹿在鹿媽媽的身后說道铲掐。 “不跑快一點(diǎn),我們會(huì)被吃掉的豪椿〈疃埽”鹿媽媽回頭對(duì)...
    529b59b4e739閱讀 244評(píng)論 0 0