Java Serializable原理分析

在分析Serializable之前掀潮,先來看看ObjectInputStream和ObjectOutputStream這兩個流對象兼蜈。

ObjectOutputStream:將對象轉(zhuǎn)化為流
ObjectInputStream:將流轉(zhuǎn)化為對象

下面舉個例子來說明ObjectInputStream和ObjectOutputStream的使用儒老。

public class Phone {

    public String name;
    public String address;

    public Phone() {
        System.out.println("=============1");
    }

    public Phone(String name, String address) {
        System.out.println("=============2");
        this.name = name;
        this.address = address;
    }
}

Phone類是測試類,調(diào)用方式如下:

public class MyClass {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        File file = new File("javatest/data/phone.txt");

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
        Phone phone = new Phone("zhangsan", "beijing");
        objectOutputStream.writeObject(phone);
        objectOutputStream.close();

        System.out.println("========================");

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        Object object = objectInputStream.readObject();
        objectInputStream.close();
        if (object instanceof Phone) {
            Phone phone1 = (Phone) object;
            System.out.println(phone1.name);
        }
        System.out.println(object);
    }
}

運(yùn)行后會出現(xiàn)如下錯誤日志:

Exception in thread "main" java.io.NotSerializableException: com.example.Phone
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
    at com.example.MyClass.main(MyClass.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

日志信息提示說Phone沒有實(shí)現(xiàn)Serializable接口遣疯。按照提示將Phone方法做相應(yīng)的修改:

public class Phone implements Serializable{

    public String name;
    public String address;

    public Phone() {
        System.out.println("=============1");
    }

    public Phone(String name, String address) {
        System.out.println("=============2");
        this.name = name;
        this.address = address;
    }
}

繼續(xù)執(zhí)行main()方法休讳,查看日志信息:

=============2
========================
zhangsan
com.example.Phone@4dd8dc3

main()方法的主要流程分為如下幾步:
1 創(chuàng)建文件phone.txt
2 創(chuàng)建Phone實(shí)例,并初始化赵哲,然后通過ObjectOutputStream將Phone的實(shí)例寫入到phone.txt中
3 通過ObjectInputStream將phone.txt中的內(nèi)容讀出,并構(gòu)造Object的一個實(shí)例君丁,將數(shù)據(jù)放入Object的實(shí)例中枫夺。
phone.txt文件中的內(nèi)容如下:

aced 0005 7372 0011 636f 6d2e 6578 616d
706c 652e 5068 6f6e 6551 4868 16d4 8afd
8702 0002 4c00 0761 6464 7265 7373 7400
124c 6a61 7661 2f6c 616e 672f 5374 7269
6e67 3b4c 0004 6e61 6d65 7100 7e00 0178
7074 0007 6265 696a 696e 6774 0008 7a68
616e 6773 616e 

如上是以byte的形式打開,下面來分析一下各個數(shù)據(jù)代表了什么意思(一下數(shù)據(jù)均為16進(jìn)制绘闷,省略了ox前綴):
aced:STREAM_MAGIC橡庞,聲明使用了序列化協(xié)議
0005:STREAM_VERSION,序列化協(xié)議版本
73:TC_OBJECT印蔗,聲明這是一個新的對象
72:TC_CLASSDESC扒最,聲明這里開始一個新Class
0011:Class名字的長度
636f 6d2e 6578 616d 706c 652e 5068 6f6e 65:類名,com.example.Phone
51 4868 16d4 8afd 87:SerialVersionUID, 序列化ID华嘹,如果沒有指定吧趣, 則會由算法隨機(jī)生成一個8byte的ID。
02:標(biāo)記號耙厚,該值聲明該對象支持序列化
0002:該類所包含的域個數(shù)
4c:L
00 07:域名字的長度
61 6464 7265 7373:address
74:TC_STRING强挫,代表一個new String,用String來引用對象
00 12:長度
4c:L
6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b4c:java/lang/String;L
0004:域名字的長度
6e61 6d65:name
7100 7e00 01:待確認(rèn)
78:TC_ENDBLOCKDATA對象塊結(jié)束的標(biāo)志
70:沒有超類
74:TC_STRING
0007:address字段值的長度
6265 696a 696e 67:address字段的值beijing
74:TC_STRING
0008:name字段值的長度
7a68 616e 6773 616e : name字段的值zhangsan
文件中的內(nèi)容相當(dāng)豐富薛躬,僅僅憑這些內(nèi)容俯渤,已經(jīng)可以完整的復(fù)原Phone實(shí)例存儲前的狀態(tài)。
其實(shí)型宝,如上過程八匠,就是Java 序列化和反序列化的一個簡單的實(shí)現(xiàn)絮爷。
Serializable也能夠?qū)崿F(xiàn)自定義序列化過程。
重新定義Phone類如下:

public class Phone implements Serializable {

    transient public String name;
    public String address;

    public Phone() {
        System.out.println("=============1");
    }

    public Phone(String name, String address) {
        System.out.println("=============2");
        this.name = name;
        this.address = address;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeBytes(name);
        System.out.println("=========write");
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        byte[] bytes = new byte[50];
        in.read(bytes);
        name = new String(bytes).trim();
        System.out.println("=========read");
    }
    
}

將name屬性添加了transient修飾梨树,添加了兩個方法writeObject()和readObject()坑夯。運(yùn)行main()方法,日志信息如下:

=============2
=========write
========================
=========read
zhangsan
com.example.Phone@58372a00

關(guān)于Serializable的使用劝萤,有幾點(diǎn)需要說明:
1 Serializable只是一個接口渊涝,本身沒有任何實(shí)現(xiàn)
2 對象的反序列化并沒有調(diào)用對象的任何構(gòu)造方法
3 serialVersionUID是用于記錄文件版本信息的,最好能夠自定義床嫌。否則跨释,系統(tǒng)會自動生成一個serialVersionUID,文件或者對象的任何改變厌处,都會改變serialVersionUID鳖谈,導(dǎo)致反序列化的失敗,如果自定義就沒有這個問題阔涉。
4 如果某個屬性不想實(shí)現(xiàn)序列化缆娃,可以采用transient修飾
5 Serializable的系統(tǒng)實(shí)現(xiàn)是采用ObjectInputStream和ObjectOutputStream實(shí)現(xiàn)的,這也是為什么調(diào)用ObjectInputStream和ObjectOutputStream時瑰排,需要對應(yīng)的類實(shí)現(xiàn)Serializable接口贯要。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市椭住,隨后出現(xiàn)的幾起案子崇渗,更是在濱河造成了極大的恐慌,老刑警劉巖京郑,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宅广,死亡現(xiàn)場離奇詭異,居然都是意外死亡些举,警方通過查閱死者的電腦和手機(jī)跟狱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來户魏,“玉大人驶臊,你說我怎么就攤上這事〉鸪螅” “怎么了资铡?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長幢码。 經(jīng)常有香客問我笤休,道長,這世上最難降的妖魔是什么症副? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任店雅,我火速辦了婚禮政基,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘闹啦。我一直安慰自己沮明,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布窍奋。 她就那樣靜靜地躺著荐健,像睡著了一般。 火紅的嫁衣襯著肌膚如雪琳袄。 梳的紋絲不亂的頭發(fā)上江场,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音窖逗,去河邊找鬼址否。 笑死,一個胖子當(dāng)著我的面吹牛碎紊,可吹牛的內(nèi)容都是我干的佑附。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼仗考,長吁一口氣:“原來是場噩夢啊……” “哼音同!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起秃嗜,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤权均,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痪寻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體螺句,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡虽惭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年橡类,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芽唇。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡顾画,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出匆笤,到底是詐尸還是另有隱情研侣,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布炮捧,位于F島的核電站庶诡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咆课。R本人自食惡果不足惜末誓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一扯俱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喇澡,春花似錦迅栅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至呕屎,卻和暖如春让簿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背榨惰。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工拜英, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人琅催。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓居凶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親藤抡。 傳聞我的和親對象是個殘疾皇子侠碧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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