java-序列化與反序列化

序列化和反序列化的概念

  1. 序列化:把java對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化,這些字節(jié)序列可以被保存在磁盤上或通過網(wǎng)絡(luò)傳輸,以備以后重新恢復原來的對象
  2. 反序列化:把字節(jié)序列恢復為對象的過程稱為對象的反序列化命斧。序列化機制使得對象可以脫離程序的運行而獨立存在

序列化的功能/用途

  1. 持久化對象:Java平臺允許我們在內(nèi)存中創(chuàng)建可復用的Java對象田晚,但一般情況下,只有當JVM處于運行時国葬,這些對象才可能存在贤徒,即碍讯,
    這些對象的生命周期不會比JVM的生命周期更長脖咐。但在現(xiàn)實應(yīng)用中,就可能要求在JVM停止運行之后能夠保存(持久化)指定的對象兼蜈,并在將來重新讀取被保存的對象通孽。把對象的字節(jié)序列永久地保存到硬盤上序宦,通常存放在一個文件中,以此實現(xiàn)該功能 背苦。java中的對象的內(nèi)部狀態(tài)只保存在內(nèi)存中互捌,其生命周期最長與JVM的生命周期一樣,即JVM停止之后行剂,所有對象都會被銷毀秕噪。
  2. 網(wǎng)絡(luò)傳輸:在網(wǎng)絡(luò)上傳送對象的字節(jié)序列。

實際應(yīng)用

  1. 在很多應(yīng)用中厚宰,需要對某些對象進行序列化腌巾,讓它們離開內(nèi)存空間,入住物理硬盤,以便長期保存壤躲。比如最常見的是Web服務(wù)器中的Session對象城菊,當有 10萬用戶并發(fā)訪問备燃,就有可能出現(xiàn)10萬個Session對象碉克,內(nèi)存可能吃不消,于是Web容器就會把一些seesion先序列化到硬盤中并齐,等要用了漏麦,再把保存在硬盤中的對象還原到內(nèi)存中。
  2. 當兩個進程在進行遠程通信時况褪,彼此可以發(fā)送各種類型的數(shù)據(jù)撕贞。無論是何種類型的數(shù)據(jù),都會以二進制序列的形式在網(wǎng)絡(luò)上傳送测垛。發(fā)送方需要把這個Java對象轉(zhuǎn)換為字節(jié)序列捏膨,才能在網(wǎng)絡(luò)上傳送;接收方則需要把字節(jié)序列再恢復為Java對象食侮。

實現(xiàn)

  1. java.io.Serializable接口号涯,那么它就可以被序列化
  2. Externalizable:
    Serializable接口
    · 優(yōu)點:內(nèi)建支持
    · 優(yōu)點:易于實現(xiàn)
    · 缺點:占用空間過大
    · 缺點:由于額外的開銷導致速度變比較慢
    Externalizable接口
    · 優(yōu)點:開銷較少(程序員決定存儲什么)
    · 優(yōu)點:可能的速度提升
    · 缺點:虛擬機不提供任何幫助,也就是說所有的工作都落到了開發(fā)人員的肩上锯七。
    在兩者之間如何選擇要根據(jù)應(yīng)用程序的需求來定链快。Serializable通常是最簡單的解決方案,但是它可能會導致出現(xiàn)不可接受的性能問題或空間問題眉尸;在出現(xiàn)這些問題的情況下域蜗,Externalizable可能是一條可行之路。

JDK類庫中的序列化API

  1. java.io.ObjectOutputStream代表對象輸出流噪猾,它的writeObject(Object obj)方法可對參數(shù)指定的obj對象進行序列化霉祸,把得到的字節(jié)序列寫到一個目標輸出流中。
  2. java.io.ObjectInputStream代表對象輸入流袱蜡,它的readObject()方法從一個源輸入流中讀取字節(jié)序列丝蹭,再把它們反序列化為一個對象,并將其返回戒劫。

序列化與反序列化的編程實現(xiàn)

實現(xiàn)序列化接口的類

public class Person implements Serializable{
    private static final long serialVersionUID = -1015228989208411177L;
    private String name;   
        private  int age;  
    public Person(String name, int age) {    
        this.name = name;   
        this.age = age;   
    }   
    public String getName() {   
        return name;   
    }   
    public void setName(String name) {   
        this.name = name;   
    }      
    public int getAge() {   
        return age;   
    }      
    public void setAge(int age) {   
        this.age = age;   
    }   
}

序列化過程

public class WriteObject {
    public static void main(String[] args) {
        ObjectOutputStream oos = null;   
        try {   
            //1.創(chuàng)建一個ObjectOutputStream   
            oos = new ObjectOutputStream(new FileOutputStream("/home/sunyan/object.txt"));   
            Person per = new Person("孫悟空", 500);  
            //2.將per對象寫入輸入流   
            oos.writeObject(per); 
        } catch (FileNotFoundException e) {   
             e.printStackTrace();   
        } catch (IOException e) {   
             e.printStackTrace();   
        }finally{   
            try {   
                if(oos != null){   
                    oos.close();   
                }   
            } catch (IOException e) {   
                 e.printStackTrace();   
            }   
        }   
    }
}

反序列化

public class ReadObject {
    public static void main(String[] args) {
        ObjectInputStream ois = null;              
        try {   
            //1.創(chuàng)建一個ObjectInputStream輸入流   
           ois = new ObjectInputStream(new FileInputStream("/home/sunyan/object.txt"));   
            //2.從輸入流中讀取一個Java對象半夷,并將其強制類型轉(zhuǎn)換為Person對象   
            Person p = (Person) ois.readObject();
            System.out.println("名字為:" + p1.getName() + "\n年齡為:" + p1.getAge());
        } catch (FileNotFoundException e) {   
            e.printStackTrace();   
        } catch (IOException e) {   
            e.printStackTrace();   
        } catch (ClassNotFoundException e) {   
            e.printStackTrace();   
        }finally{   
            try {   
                if (ois == null) {   
                     ois.close();   
                }   
            } catch (IOException e) {   
                e.printStackTrace();   
            }   
        }   
    }
}

執(zhí)行結(jié)果:

  1. 如果我們向文件中使用序列化機制寫入了多個Java對象,使用反序列化機制恢復對象必須按照實際寫入的順序讀取迅细。
    序列化
 Person per1 = new Person("孫悟空", 500);  
 Person per2 = new Person("孫小妹", 50); 
oos.writeObject(per1); 
 oos.writeObject(per2); 

反序列化

Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();   
System.out.println("名字為:" + p1.getName() + "\n年齡為:" + p1.getAge());
System.out.println("名字為:" + p2.getName() + "\n年齡為:" + p2.getAge()); 

執(zhí)行結(jié)果


  1. 對象引用的序列化
    如果類的屬性不是基本類型或者String類型巫橄,而是另一個引用類型,那么這個引用類型必須是可序列化的茵典,否則該類也是不可序列化的湘换,即使該類實現(xiàn)了Serializable,Externalizable接口。
public class Teacher implements Serializable{
    private String name;   
    //類的屬性是引用類型彩倚,也必須序列化筹我。   
    //如果Person是不可序列化的,無論Teacher實現(xiàn)Serializable,Externalizable接口帆离,則Teacher   
    //都是不可序列化的蔬蕊。   
    private Person student;   
    public Teacher(String name, Person student) {   
        super();   
        this.name = name;   
        this.student = student;   
    }   
    public String getName() {   
         return name;   
    }    
    public void setName(String name) {   
         this.name = name;   
    }      
    public Person getStudent() {   
         return student;   
    }    
    public void setStudent(Person student) {   
        this.student = student;   
    }   
}

上述代碼中,Teacher中有一個引用類型student,若Person未實現(xiàn)接口Serializable哥谷。即

public class Person implements Serializable{
}

則在序列化過程中岸夯,會報錯


  1. Transient 關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字们妥,可以阻止該變量被序列化到文件中猜扮,在被反序列化后,transient 變量的值被設(shè)為初始值监婶,如 int 型的是 0旅赢,對象型的是 null。
    在Person中更改如下代碼
private  transient int age; 

此時1中的代碼惑惶,經(jīng)序列化和反序列化后煮盼,執(zhí)行結(jié)果如下


  1. s?e?r?i?a?l?V?e?r?s?i?o?n?U?I?D
    s?e?r?i?a?l?V?e?r?s?i?o?n?U?I?D?:? ?字?面?意?思?上?是?序?列?化?的?版?本?號?,凡是實現(xiàn)Serializable接口的類都有一個表示序列化版本標識符的靜態(tài)變量集惋。序列化 ID 是否一致孕似,決定 虛擬機是否允許反序列化。
    實現(xiàn)Serializable接口的類如果類中沒有添加serialVersionUID喉祭,那么就會出現(xiàn)如下的警告提示



    serialVersionUID有兩種生成方式:
    采用 Add default serial version ID
    這種方式生成的serialVersionUID是1L,例如:

 private static final long serialVersionUID = 1L;

采用 Add generated serial version ID這種方式生成的serialVersionUID是根據(jù)類名雷绢,接口名,方法和屬性等來生成的翘紊,例如:

private static final long serialVersionUID = -1015228989208411177L;

對1中的代碼序列化后,修改

private static final long serialVersionUID = -1015228989208411177L;

private static final long serialVersionUID = -1015228989208411178L;

此時帆疟,進行反序列化,會報錯


反序列化漏洞危害

當應(yīng)用代碼從用戶接受序列化數(shù)據(jù)踪宠,并試圖反序列化改數(shù)據(jù)進行下一步處理時,會產(chǎn)生反序列化漏洞柳琢,其中最有危害性的就是遠程代碼注入润脸。
這種漏洞產(chǎn)生原因是,java類ObjectInputStream在執(zhí)行反序列化時他去,并不會對自身的輸入進行檢查,這就說明惡意攻擊者可能也可以構(gòu)建特定的輸入灾测,在 ObjectInputStream類反序列化之后會產(chǎn)生非正常結(jié)果爆价,利用這一方法就可以實現(xiàn)遠程執(zhí)行任意代碼行施。

最后再加一些相關(guān)知識點

1允坚、聲明為static和transient的成員數(shù)據(jù)不能被串行化魂那,因為static代表類的狀態(tài),transient代表對象的臨時數(shù)據(jù)蛾号。

2、要想將父類對象也序列化涯雅,就需要讓父類也實現(xiàn)Serializable 接口鲜结。
3、服務(wù)器端給客戶端發(fā)送序列化對象數(shù)據(jù)活逆,對象中有一些數(shù)據(jù)是敏感的精刷,比如密碼字符串等,希望對該密碼字段在序列化時蔗候,進行加密怒允,而客戶端如果擁有解密的密鑰,只有在客戶端進行反序列化時锈遥,才可以對密碼進行讀取纫事,這樣可以一定程度保證序列化對象的數(shù)據(jù)安全。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末所灸,一起剝皮案震驚了整個濱河市丽惶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌爬立,老刑警劉巖钾唬,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異侠驯,居然都是意外死亡抡秆,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門吟策,熙熙樓的掌柜王于貴愁眉苦臉地迎上來儒士,“玉大人,你說我怎么就攤上這事踊挠≌Ч穑” “怎么了冲杀?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長睹酌。 經(jīng)常有香客問我权谁,道長,這世上最難降的妖魔是什么憋沿? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任旺芽,我火速辦了婚禮,結(jié)果婚禮上辐啄,老公的妹妹穿的比我還像新娘采章。我一直安慰自己,他們只是感情好壶辜,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布悯舟。 她就那樣靜靜地躺著砸民,像睡著了一般。 火紅的嫁衣襯著肌膚如雪反惕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天姿染,我揣著相機與錄音悬赏,去河邊找鬼程癌。 笑死嵌莉,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的中鼠。 我是一名探鬼主播沿癞,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼椎扬,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了筐赔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤达皿,失蹤者是張志新(化名)和其女友劉穎峦椰,沒想到半個月后汰规,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了冒签。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡刚梭,死狀恐怖朴读,靈堂內(nèi)的尸體忽然破棺而出走趋,到底是詐尸還是另有隱情,我是刑警寧澤氮唯,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布惩琉,位于F島的核電站瞒渠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伍玖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一始赎、第九天 我趴在偏房一處隱蔽的房頂上張望造垛。 院中可真熱鬧晰搀,春花似錦、人聲如沸杆逗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至癣疟,卻和暖如春潮酒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扎狱。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工委乌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荣回,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓壕吹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親踏堡。 傳聞我的和親對象是個殘疾皇子咒劲,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

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

  • 一腐魂、 序列化和反序列化概念 Serialization(序列化)是一種將對象以一連串的字節(jié)描述的過程;反序列化de...
    步積閱讀 1,444評論 0 10
  • 原帖地址:原帖個人網(wǎng)站地址:個人網(wǎng)站簡書對markdown的支持太完美了削樊,我竟然可以直接Ctrl C/V過來漫贞。 定...
    ryderchan閱讀 3,807評論 1 9
  • 簡介 對于一個存在于Java虛擬機中的對象來說育叁,其內(nèi)部的狀態(tài)只保持在內(nèi)存中。JVM停止之后仪际,這些狀態(tài)就丟失了昵骤。在很...
    FX_SKY閱讀 802評論 0 0
  • 問題 Java序列化與反序列化是什么变秦?為什么需要序列化與反序列化蹦玫?有什么好處刘绣?如何實現(xiàn)Java序列化與反序列化? ...
    海邊的卡夫卡丶閱讀 387評論 1 1
  • 序列化的意義 1.永久存儲某個jvm中運行時的對象福贞。2.對象可以網(wǎng)絡(luò)傳輸3.rmi調(diào)用都是以序列化的方式傳輸參數(shù) ...
    炫邁哥閱讀 656評論 0 0