java-序列化與反序列化

序列化和反序列化的概念

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

序列化的功能/用途

  1. 持久化對象:Java平臺允許我們在內存中創建可復用的Java對象,但一般情況下,只有當JVM處于運行時,這些對象才可能存在,即,
    這些對象的生命周期不會比JVM的生命周期更長。但在現實應用中,就可能要求在JVM停止運行之后能夠保存(持久化)指定的對象,并在將來重新讀取被保存的對象。把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中,以此實現該功能 。java中的對象的內部狀態只保存在內存中,其生命周期最長與JVM的生命周期一樣,即JVM停止之后,所有對象都會被銷毀。
  2. 網絡傳輸:在網絡上傳送對象的字節序列。

實際應用

  1. 在很多應用中,需要對某些對象進行序列化,讓它們離開內存空間,入住物理硬盤,以便長期保存。比如最常見的是Web服務器中的Session對象,當有 10萬用戶并發訪問,就有可能出現10萬個Session對象,內存可能吃不消,于是Web容器就會把一些seesion先序列化到硬盤中,等要用了,再把保存在硬盤中的對象還原到內存中。
  2. 當兩個進程在進行遠程通信時,彼此可以發送各種類型的數據。無論是何種類型的數據,都會以二進制序列的形式在網絡上傳送。發送方需要把這個Java對象轉換為字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復為Java對象。

實現

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

JDK類庫中的序列化API

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

序列化與反序列化的編程實現

實現序列化接口的類

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.創建一個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.創建一個ObjectInputStream輸入流   
           ois = new ObjectInputStream(new FileInputStream("/home/sunyan/object.txt"));   
            //2.從輸入流中讀取一個Java對象,并將其強制類型轉換為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();   
            }   
        }   
    }
}

執行結果:

  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()); 

執行結果


  1. 對象引用的序列化
    如果類的屬性不是基本類型或者String類型,而是另一個引用類型,那么這個引用類型必須是可序列化的,否則該類也是不可序列化的,即使該類實現了Serializable,Externalizable接口。
public class Teacher implements Serializable{
    private String name;   
    //類的屬性是引用類型,也必須序列化。   
    //如果Person是不可序列化的,無論Teacher實現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未實現接口Serializable。即

public class Person implements Serializable{
}

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


  1. Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設為初始值,如 int 型的是 0,對象型的是 null。
    在Person中更改如下代碼
private  transient int age; 

此時1中的代碼,經序列化和反序列化后,執行結果如下


  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?:? ?字?面?意?思?上?是?序?列?化?的?版?本?號?,凡是實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量。序列化 ID 是否一致,決定 虛擬機是否允許反序列化。
    實現Serializable接口的類如果類中沒有添加serialVersionUID,那么就會出現如下的警告提示



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

 private static final long serialVersionUID = 1L;

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

private static final long serialVersionUID = -1015228989208411177L;

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

private static final long serialVersionUID = -1015228989208411177L;

private static final long serialVersionUID = -1015228989208411178L;

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


反序列化漏洞危害

當應用代碼從用戶接受序列化數據,并試圖反序列化改數據進行下一步處理時,會產生反序列化漏洞,其中最有危害性的就是遠程代碼注入。
這種漏洞產生原因是,java類ObjectInputStream在執行反序列化時,并不會對自身的輸入進行檢查,這就說明惡意攻擊者可能也可以構建特定的輸入,在 ObjectInputStream類反序列化之后會產生非正常結果,利用這一方法就可以實現遠程執行任意代碼。

最后再加一些相關知識點

1、聲明為static和transient的成員數據不能被串行化,因為static代表類的狀態,transient代表對象的臨時數據。

2、要想將父類對象也序列化,就需要讓父類也實現Serializable 接口。
3、服務器端給客戶端發送序列化對象數據,對象中有一些數據是敏感的,比如密碼字符串等,希望對該密碼字段在序列化時,進行加密,而客戶端如果擁有解密的密鑰,只有在客戶端進行反序列化時,才可以對密碼進行讀取,這樣可以一定程度保證序列化對象的數據安全。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容

  • 一、 序列化和反序列化概念 Serialization(序列化)是一種將對象以一連串的字節描述的過程;反序列化de...
    步積閱讀 1,453評論 0 10
  • 原帖地址:原帖個人網站地址:個人網站簡書對markdown的支持太完美了,我竟然可以直接Ctrl C/V過來。 定...
    ryderchan閱讀 3,819評論 1 9
  • 簡介 對于一個存在于Java虛擬機中的對象來說,其內部的狀態只保持在內存中。JVM停止之后,這些狀態就丟失了。在很...
    FX_SKY閱讀 809評論 0 0
  • 問題 Java序列化與反序列化是什么?為什么需要序列化與反序列化?有什么好處?如何實現Java序列化與反序列化? ...
    海邊的卡夫卡丶閱讀 388評論 1 1
  • 序列化的意義 1.永久存儲某個jvm中運行時的對象。2.對象可以網絡傳輸3.rmi調用都是以序列化的方式傳輸參數 ...
    炫邁哥閱讀 663評論 0 0