Java序列化總結(jié)

Java序列化總結(jié)

序列化就是指將Java對象轉(zhuǎn)換成一系列的字節(jié)秒裕,反序列化即使將一系列的字節(jié)恢復成Java對象。序列話的主要用途在于:

  1. 持久化對象 - 可以將對象輸出的磁盤等介質(zhì)上
  2. 跨進程調(diào)用 - 不同進程無法相互訪問對方的地址空間丹皱,可以通過序列化+IPC在不同進程間共享對象
  3. 通過網(wǎng)絡(luò)傳輸對象 - 將對象序列化后通過網(wǎng)絡(luò)傳輸,以實現(xiàn)客戶端和服務(wù)器之間共享對象

Serializable接口的使用

Java提供了Serialzable接口用于序列化,Serilizable接口是一個標記接口产上,實現(xiàn)這個對象的類無需事先任何方法撑蒜。

ObjectOutputStream & ObjectIntputStream

ObjectOutputStream 和 ObjectIntputStream 分別用于將對象作為字節(jié)流輸出以及將字節(jié)流轉(zhuǎn)換成對象歹啼。下面這例子有兩個java程序,一個用于寫每一個用于讀:

  1. 測試類
package com.minghui.test;

import java.io.Serializable;

class TestClass implements Serializable {
    public static final long serialVersionUID = 1L;
    public static String sStaticField = "static field";
    public String mName = "TestClass";
}

讀程序和寫程序分別都需要包辦這個類座菠。

  1. 寫程序
public class SerilizationWriteTest {

    public static void main(String[] args) {
        TestClass myTest = new TestClass();
        myTest.mName = "New Name";
        TestClass.sStaticField = "New Static Value";
        ObjectOutputStream output = null;
        try {
            output = new ObjectOutputStream(new FileOutputStream("D:/result"));
            output.writeObject(myTest);
            output.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (output != null) {
                try {
                    output.close();
                } catch (IOException e) {
                }
            }
        }
    }
}
  1. 讀程序
public class SerilazationReadTest {

    public static void main(String[] args) {
        ObjectInputStream input = null;
        try {
            input = new ObjectInputStream(new FileInputStream("D:/result"));
            TestClass test = (TestClass) input.readObject();
            System.out.println("SerilazationREAD name:" + test.mName);
            System.out.println("SerilazationREAD sStaticField:" + TestClass.sStaticField);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

讀程序輸出結(jié)果為:

SerilazationREAD name:New Name

SerilazationREAD sStaticField:static field
需要注意的一點是狸眼,靜態(tài)變量是不參與序列化的,在上面的例子中寫程序?qū)o態(tài)變量的復制并不會影響讀程序中靜態(tài)變量的值浴滴。

類的成員的序列化

如果類中的某些成員無法被序列化會出現(xiàn)什么情況呢?這里為TestClass添加一個未繼承Serilizable的成員:

OtherClass mOtherField = new OtherClass();

public class OtherClass {
    public int x = 0;
    public int y = 0;

    OtherClass() {
        x = 2;
        y = 2;
    }
}

修改寫程序:

public static void main(String[] args) {
    TestClass myTest = new TestClass();
    myTest.mName = "New Name";
    myTest.mOtherField.x = 1;
    myTest.mOtherField.y = 1;
    TestClass.sStaticField = "New Static Value";
    ObjectOutputStream output = null;
    try {
        output = new ObjectOutputStream(new FileOutputStream("D:/result"));
        output.writeObject(myTest);
        output.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (output != null) {
            try {
                output.close();
            } catch (IOException e) {
            }
        }
    }
}

輸出結(jié)果如下:

java.io.NotSerializableException: com.minghui.test.OtherClass

serialVersionUID

在上面的例子中拓萌,修改讀程序TestClass中的serialVersionUID:

public static final long serialVersionUID = 2L;

結(jié)果如下:

java.io.InvalidClassException: com.minghui.test.TestClass; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.minghui.test.SerilazationReadTest.main(SerilazationReadTest.java:15)

在反序列化的過程中,虛擬機會對比恢復字節(jié)流中的serialVersionUID和本地類中的serialVersionUID巡莹,僅當相同時才認為兩者是相同的類司志。

子類的序列化

如果一個類A繼承自類B,A實現(xiàn)了Serializable接口降宅,B未實現(xiàn)骂远。那么子類中的成員可以被序列化,父類中的成員無法被序列化腰根,反序列化后父類成員的初始值規(guī)則如下:

  1. 如果父類有默認構(gòu)造函數(shù)激才,則初始值為默認構(gòu)造函數(shù)中的賦值
  2. 如果父類沒有默認構(gòu)造函數(shù),則初始值為默認值

修改TestClass使其繼承與TestParent:

class TestClass extends TestParent implements Serializable {
    public static final long serialVersionUID = 1L;
    public static String sStaticField = "static field";
    public String mName = "TestClass";  
}

public class TestParent {
    public String mParentField = "Uinitialized";
}

修改寫程序,增加為mParentField賦值的語句:

public class SerilizationWriteTest {

    public static void main(String[] args) {
        TestClass myTest = new TestClass();
        myTest.mName = "New Name";
        myTest.mParentField = "Runtime Changed";
        TestClass.sStaticField = "New Static Value";
        //... 同之前的例子
}

修改都程序的中的log打尤衬铡:

public class SerilazationReadTest {

    public static void main(String[] args) {
        ObjectInputStream input = null;
        try {
            input = new ObjectInputStream(new FileInputStream("D:/result"));
            TestClass test = (TestClass) input.readObject();
            System.out.println("SerilazationREAD name:" + test.mName);
            System.out.println("SerilazationREAD parent field:" + test.mParentField);
            System.out.println("SerilazationREAD sStaticField:" + TestClass.sStaticField);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果如下:

SerilazationREAD name:New Name

SerilazationREAD parent field:Uinitialized

SerilazationREAD sStaticField:static field

為TestParent增加默認構(gòu)造函數(shù)劣挫,輸出如下:

SerilazationREAD name:New Name

SerilazationREAD parent field:Constructor Initialized

SerilazationREAD sStaticField:static field

序列化過程的控制

序列化允許將對象變成字節(jié)流傳輸,有時為了安全等方面的考慮东帅,并不需要對所有字段進行序列化压固,有時需要在序列化的過程中對部分字段做特殊處理(例如加密/解密),JDK提供了一系列方法用于控制序列化的過程靠闭。

transient關(guān)鍵字

當成員修飾符為為transient時帐我,JDK不會序列化該成員,TestClass中的mName增加transient修飾符

class TestClass implements Serializable {
    public static final long serialVersionUID = 1L;
    public static String sStaticField = "static field";
    public transient String mName = "TestClass";

    public TestClass() {
        mName = "constructor init";
    }
}

輸出如下:

SerilazationREAD name:null

SerilazationREAD sStaticField:static field

結(jié)合前面一節(jié)愧膀,可以總結(jié)出拦键,避免成員變量被序列化的方法有兩種:

  1. 將不希望被序列化的變量放到父類
  2. 為不希望被序列化的變量添加transient修飾符

WriteObject & ReadObject

實現(xiàn)了Serilaizable接口的類,可以通過添加WriteObject和ReadObject方法來控制序列化的過程檩淋,例子如下:

class TestClass implements Serializable {
    public static final long serialVersionUID = 1L;
    public static String sStaticField = "static field";
    public String mName = "TestClass";

    private void writeObject(ObjectOutputStream out) {
        try {
            PutField pt = out.putFields();
            System.out.println("Before encypt name :" + mName);
            String name = encyptString(mName);
            System.out.println("after encypt name :" + name);
            pt.put("Name", name);
            out.writeFields();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void readObject(ObjectInputStream in) {
        try {
            GetField gt = in.readFields();
            String name = (String) gt.get("mName", "");
            System.out.println("Before decypt name :" + name);
            mName = decyptString(name);
            System.out.println("after decypt name :" + mName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String encyptString(String original) {
        StringBuilder sb = new StringBuilder();
        int len = original.length();
        for (int i = 0; i < len; i++) {
            char c = (char) (original.charAt(i) + 1);
            sb.append(c);
        }
        return sb.toString();
    }

    private static String decyptString(String original) {
        StringBuilder sb = new StringBuilder();
        int len = original.length();
        for (int i = 0; i < len; i++) {
            char c = (char) (original.charAt(i) - 1);
            sb.append(c);
        }
        return sb.toString();
    }
}

寫程序的輸出如下:

Before encypt name :New Name

After encypt name :Ofx!Obnf

讀程序的輸出如下:

Before decypt name :Ofx!Obnf

after decypt name :New Name

SerilazationREAD name:New Name

SerilazationREAD sStaticField:static field

需要注意的一點是,GetField的get方法以及PutField的put方法芬为,第一個參數(shù)都是類中成員變量的名稱。

如果將mName添加transient蟀悦,結(jié)果是什么媚朦?

Exception in thread "main" java.lang.IllegalArgumentException: no such field mName with type class java.lang.Object

總結(jié)

  1. 實現(xiàn)Serilzable的類可以被序列化/反序列化
  2. 實現(xiàn)Serilzable的類的成員必須都也實現(xiàn)了Serilzable接口
  3. 如果父類未實現(xiàn)Serilzable接口,子類實現(xiàn)了Serilzable接口熬芜,反序列化時莲镣,父類成員的值為默認構(gòu)造函數(shù)中的賦值,或默認值
  4. transient修飾的關(guān)鍵字不能被序列化
  5. 可以通過為類添加WriteObject & ReadObject控制序列化/反序列化過程
  6. 使用WriteObject & ReadObject序列化/反序列化時無法控制transient修飾的成員
  7. 使用WriteObject & ReadObject序列化/反序列化時涎拉,可以繞過條目2種的限制
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瑞侮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鼓拧,更是在濱河造成了極大的恐慌半火,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件季俩,死亡現(xiàn)場離奇詭異钮糖,居然都是意外死亡,警方通過查閱死者的電腦和手機酌住,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門店归,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人酪我,你說我怎么就攤上這事消痛。” “怎么了都哭?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵秩伞,是天一觀的道長逞带。 經(jīng)常有香客問我,道長纱新,這世上最難降的妖魔是什么展氓? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮脸爱,結(jié)果婚禮上遇汞,老公的妹妹穿的比我還像新娘。我一直安慰自己簿废,他們只是感情好勺疼,可當我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捏鱼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酪耕。 梳的紋絲不亂的頭發(fā)上导梆,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音迂烁,去河邊找鬼看尼。 笑死,一個胖子當著我的面吹牛盟步,可吹牛的內(nèi)容都是我干的藏斩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼却盘,長吁一口氣:“原來是場噩夢啊……” “哼狰域!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起黄橘,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤兆览,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后塞关,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抬探,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年帆赢,在試婚紗的時候發(fā)現(xiàn)自己被綠了小压。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡椰于,死狀恐怖怠益,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情廉羔,我是刑警寧澤溉痢,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布僻造,位于F島的核電站,受9級特大地震影響孩饼,放射性物質(zhì)發(fā)生泄漏髓削。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一镀娶、第九天 我趴在偏房一處隱蔽的房頂上張望立膛。 院中可真熱鬧,春花似錦梯码、人聲如沸宝泵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儿奶。三九已至,卻和暖如春鳄抒,著一層夾襖步出監(jiān)牢的瞬間闯捎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工许溅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瓤鼻,地道東北人。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓贤重,卻偏偏與公主長得像茬祷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子并蝗,可洞房花燭夜當晚...
    茶點故事閱讀 44,947評論 2 355

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