<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    walterwing  
    日歷
    <2008年10月>
    2829301234
    567891011
    12131415161718
    19202122232425
    2627282930311
    2345678
    統計
    • 隨筆 - 12
    • 文章 - 1
    • 評論 - 7
    • 引用 - 0

    導航

    常用鏈接

    留言簿(1)

    隨筆分類

    隨筆檔案

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

     
    本文主體內容轉載自http://www.tkk7.com/amigoxie/archive/2007/09/16/145465.html,同時也根據自己的學習體會,參考多方面資料,對其加以補充



    當兩個進程在進行遠程通信時,彼此可以發送各種類型的數據。無論是何種類型的數據,都會以二進制序列的形式在網絡上傳送。發送方需要把這個Java對象轉換為字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復為Java對象。

    Java對象轉換為字節序列的過程稱為對象的序列化

    把字節序列恢復為Java對象的過程稱為對象的反序列化。


    -----------以下內容節選自《Thinking in java 3rd Edition》-------------
         

        利用對象序列化可以實現“輕量級持久化”(lightweight persistence)。“持久化”意味著一個對象的生存周期并不取決于程序是否正在執行;它可以生存于程序的調用之間。通過將一個序列化對象寫入磁盤,然后在重新調用時恢復該對象,就能夠實現持久化的效果。之所以稱其為“輕量級”,是因為不能用某種“persistent”(持久)關鍵字來簡單地定義一個對象,并讓系統自動維護其他細節問題(盡管將來有可能實現)。相反,對象必須在程序中顯式地序列化和重組。如果需要一個更嚴格的持久化機制,可以考慮使用Java數據對象(JDO)或者像Hibernate之類的工具

        對象序列化的概念加入到語言中是為了提供對兩種主要特性的支持:

        ·Java的“遠程方法調用”(RMI,Remote Method Invocation)使存活于其他計算機上的對象使用起來就像是存活于本機上一樣。當向遠程對象發送消息時,需要通過對象序列化來傳輸參數和返回值。

    ·對Java Beans來說對象序列化也是必需的。使用一個Bean時,一般情況下是在設計階段對它的狀態信息進行配置。這種狀態信息必須保存下來,并在程序啟動以后,進行恢復;具體工作由對象序列化完成。

    -----------------------------------------------


        對象的序列化主要有兩種用途:

    1) 把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中;

    2) 在網絡上傳送對象的字節序列。

    一.             JDK類庫中的序列化API

    java.io.ObjectOutputStream代表對象輸出流,它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的字節序列寫到一個目標輸出流中。

    java.io.ObjectInputStream代表對象輸入流,它的readObject()方法從一個源輸入流中讀取字節序列,再把它們反序列化為一個對象,并將其返回。

    只有實現了SerializableExternalizable接口的類的對象才能被序列化。Externalizable接口繼承自Serializable接口,實現Externalizable接口的類完全由自身來控制序列化的行為,而僅實現Serializable接口的類可以采用默認的序列化方式 。

    對象序列化包括如下步驟:

    1) 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;

    2) 通過對象輸出流的writeObject()方法寫對象。

    對象反序列化的步驟如下:

    1) 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;

    2) 通過對象輸入流的readObject()方法讀取對象。

    下面讓我們來看一個對應的例子,類的內容如下:

    import java.io.*;

    import
     java.util.Date;

    /**

     * 對象的序列化和反序列化測試類.     

     * 
    @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>

     * 
    @version
     1.0 

     * Creation date: 2007-9-15 - 下午21:45:48

     
    */


    public class ObjectSaver {

           
    /**

            * 
    @param args

            * 
    @author
     <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>

            * Creation date: 2007-9-15 - 下午21:45:37

            
    */


           
    public static void main(String[] args) throws Exception {

                  ObjectOutputStream out 
    = new
     ObjectOutputStream

                         (
    new FileOutputStream("D:""objectFile.obj"
    ));

                  
    //序列化對象


                  Customer customer 
    = new Customer("阿蜜果"24);

                  out.writeObject(
    "你好!"
    );

                  out.writeObject(
    new
     Date());

                  out.writeObject(customer);

                  out.writeInt(
    123); //寫入基本類型數據


                  out.close();

                  
    //反序列化對象

                  ObjectInputStream in 
    = new ObjectInputStream

                         (
    new FileInputStream("D:""objectFile.obj"
    ));

                  System.out.println(
    "obj1=" +
     (String) in.readObject());

                  System.out.println(
    "obj2=" +
     (Date) in.readObject());

                  Customer obj3 
    =
     (Customer) in.readObject();

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

                  
    int obj4 =
     in.readInt();

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

                  in.close();

           }


    }


    class Customer implements Serializable {

           
    private
     String name;

           
    private int
     age;

           
    public Customer(String name, int age) 
    {

                  
    this.name =
     name;

                  
    this.age =
     age;

           }


           
    public String toString() {

                  
    return "name=" + name + ", age=" +
     age;

           }


    }

     輸出結果如下:

    obj1=你好!

    obj2=Sat Sep 15 22:02:21 CST 2007

    obj3=name=阿蜜果, age=24

    obj4=123

        因此例比較簡單,在此不再詳述。


    二    實現Serializable接口

    ObjectOutputStream只能對Serializable接口的類的對象進行序列化。默認情況下,ObjectOutputStream按照默認方式序列化,這種序列化方式僅僅對對象的非transient的實例變量進行序列化,而不會序列化對象的transient的實例變量,也不會序列化靜態變量。

        當
    ObjectIntputStream按照默認方式反序列化時,具有如下特點:

    1)              如果在內存中對象所屬的類還沒有被加載,那么會先加載并初始化這個類。如果在classpath中不存在相應的類文件,那么會拋出ClassNotFoundException

    2)              在反序列化時不會調用類的任何構造方法(注意與下面Externalizable接口的區別)。

    如果用戶希望控制類的序列化方式,可以在可序列化類中提供以下形式的writeObject()readObject()方法。

    private void writeObject(java.io.ObjectOutputStream out) throws IOException

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

    需要注意的地方是:上面兩個方法并不是Serializable接口定義的,Serializable只是一個標記接口,并沒有任何內容。而且這兩個方法都是private的,但卻并不是被定義這兩個方法的類本身所調用——當ObjectOutputStream對一個Customer對象進行序列化時,如果該對象具有writeObject()方法,那么就會執行這一方法,否則就按默認方式序列化。在該對象的writeObjectt()方法中,可以先調用ObjectOutputStreamdefaultWriteObject()方法,使得對象輸出流先執行默認的序列化操作。同理可得出反序列化的情況,不過這次是defaultReadObject()方法。

    有些對象中包含一些敏感信息,這些信息不宜對外公開。如果按照默認方式對它們序列化,那么它們的序列化數據在網絡上傳輸時,可能會被不法份子竊取。對于這類信息,可以對它們進行加密后再序列化,在反序列化時則需要解密,再恢復為原來的信息——這是transient關鍵字的第一個用途,屏蔽敏感信息。

    transient的第二個功能是在用途時,將某些無需序列化的成員變量設為transient類型,將節省空間和時間,提高序列化的性能。

        transient的第三個用途是如果類的內部有某個非序列化的對象引用,可以將其標記為transient來避免拋出NotSerializableException異常

        默認的序列化方式會序列化整個對象圖,這需要遞歸遍歷對象圖。如果對象圖很復雜,遞歸遍歷操作需要消耗很多的空間和時間,它的內部數據結構為雙向列表。


        注意在遞歸遍歷過程中,對同一對象的引用如果出現多次,序列化過程并不會重復寫入多個,具體的做法如下:
        ·保存到磁盤的所有對象都獲得一個序列號(1、2、3等)
        ·當要保存一個對象時,先檢查該對象是否已經被保存了
        ·如果以前保存過,只需寫入“與已經保存的具有序列號x的對象相同”標記;否則,保存它的所有數據

        當需要讀回對象時,將上述過程簡單地逆轉即可。
        

    三        實現Externalizable接口

    Externalizable接口繼承自Serializable接口,如果一個類實現了Externalizable接口,那么將完全由這個類控制自身的序列化行為。Externalizable接口聲明了兩個方法:

    public void writeExternal(ObjectOutput out) throws IOException

    public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException

    前者負責序列化操作,后者負責反序列化操作。

    在對實現了Externalizable接口的類的對象進行反序列化時,會先調用類的不帶參數的構造方法,這是有別于默認反序列方式的。如果把類的不帶參數的構造方法刪除,或者把該構造方法的訪問權限設置為private、默認或protected級別,會拋出java.io.InvalidException: no valid constructor異常。

        
    類實現externalizable時,頭寫入對象流中,然后類完全負責序列化和恢復數據成員,除了頭以外,根本沒有自動序列化。

        這里要注意了:聲明類實現Externalizable接口會有重大的安全風險。writeExternal()與readExternal()方法聲明為public,惡意類可以用這些方法讀取和寫入對象數據。如果對象包含敏感信息,則要格外小心。這包括使用安全套接或加密整個字節流。


    四    可序列化類的不同版本的序列化兼容性

        凡是實現Serializable
    接口的類都有一個表示序列化版本標識符的靜態變量:

        private static final long serialVersionUID;


        以上
    serialVersionUID的取值是Java運行時環境根據類的內部細節自動生成的。如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID的取值有可能也會發生變化。

    類的serialVersionUID的默認值完全依賴于Java編譯器的實現,對于同一個類,用不同的Java編譯器編譯,有可能會導致不同的serialVersionUID,也有可能相同。為了提高serialVersionUID的獨立性和確定性,強烈建議在一個可序列化類中顯示的定義serialVersionUID,為它賦予明確的值。顯式地定義serialVersionUID有兩種用途:

    1              在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;

    2)              在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。


    五    利用序列化來“克隆”對象

        要知道,序列化是對對象的一個“深拷貝”,為此我們完全可以用序列化來克隆一個對象(如果支持的話)。

        要克隆序列化對象,簡單地將該對象序列化到輸出流中去,然后再讀取回來。結果就是一個對已經存在的對象進行了深拷貝的新對象。我們不需要將該對象寫入文件中——可以使用ByteArrayOutputStream將數據保存在字節數組中。

        但是,這種方法盡管很聰明,但是比創建一個新的對象,然后拷貝或克隆數據字段的克隆方法要慢許多。

        例:
        
    /**
      A class whose clone method uses serialization
    */


    class SerialCloneable implements Cloneable, Serializable
    {
        
    try 
    {
            
    // save the object to a byte array

            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream out 
    = new
     ObjectOutputStream(bout);
            out.writeObject(
    this
    );
            out.close();

            
    // read a clone of the object from the byte array

            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray);
            ObjectInputStream in 
    = new
     ObjectInputStream(bin);
            Object ret 
    =
     in.readObject();
            in.close();

            ret.close();
        }

        
    catch(Exception e) {
            
    return null
    ;
        }

    }


    /**
      Now the class Employee is able to clone itself using serialization
    */

    class Employee extends SerialCloneable {
        
    // 

    }


    六    Preferences

        JDK 1.4引入了Preferences API,它比對象序列化更接近于持久化,因為它可以自動存儲和讀取信息。不過,它只能用于小的受限的集合——我們只能存儲原始類型和字符串,并且每個字符串的存儲長度不能超過8K。正如其名,Preferences API用于存儲和讀取用戶的Preferences以及程序配置項的設置。

        Preferences是一個鍵值集合(類似映射),存儲在一個結點層次結構中。


    七    有關序列化的一些最佳實踐(轉自
    http://java.ccidnet.com/art/3737/20040111/469787_1.html

            1、實現Serializable回導致發布的API難以更改,并且使得package-private和private

    這兩個本來封裝的較好的咚咚也不能得到保障了

    2、Serializable會為每個類生成一個序列號,生成依據是類名、類實現的接口名、

    public和protected方法,所以只要你一不小心改了一個已經publish的API,并且沒有自

    己定義一個long類型的叫做serialVersionUID的field,哪怕只是添加一個getXX,就會

    讓你讀原來的序列化到文件中的東西讀不出來(不知道為什么要把方法名算進去?)

    3、不用構造函數用Serializable就可以構造對象,看起來不大合理,這被稱為

    extralinguistic mechanism,所以當實現Serializable時應該注意維持構造函數中所維

    持的那些不變狀態

    4、增加了發布新版本的類時的測試負擔

    5、1.4版本后,JavaBeans的持久化采用基于XML的機制,不再需要Serializable

    6、設計用來被繼承的類時,盡量不實現Serializable,用來被繼承的interface也不要

    繼承Serializable。但是如果父類不實現Serializable接口,子類很難實現它,特別是

    對于父類沒有可以訪問的不含參數的構造函數的時候。所以,一旦你決定不實現

    Serializable接口并且類被用來繼承的時候記得提供一個無參數的構造函數

    7、不管你選擇什么序列化形式,聲明一個顯式的UID:

    private static final long serialVersionUID = randomLongValue;

    8、不需要序列化的東西使用transient注掉它吧,別什么都留著

    9、writeObject/readObject重載以完成更好的序列化

    readResolve 與 writeReplace重載以完成更好的維護invariant controllers

            

    八        總結

    ·要想序列化,需要聲明實現Serializable接口(Exeternalizable一會再說)
    ·如果有些東西想藏起來,用transient標識
    ·如果想自己處理transient或static成員,自己定義readObject()和writeObject()
    ·如果完全想自己處理,實現Exeternalizable接口
    posted on 2008-10-29 21:19 This is Wing 閱讀(4893) 評論(0)  編輯  收藏 所屬分類: Java基礎
     
    Copyright © This is Wing Powered by: 博客園 模板提供:滬江博客
    主站蜘蛛池模板: 日本免费网址大全在线观看| 久久国产精品免费视频| 国产免费丝袜调教视频| 久久久久久久亚洲Av无码| 免费福利电影在线观看| 亚洲中文久久精品无码ww16| 暖暖在线日本免费中文| 亚洲看片无码在线视频| 4虎永免费最新永久免费地址| 亚洲激情视频网站| 免费国产黄线在线观看| 亚洲 日韩 色 图网站| 国产嫩草影院精品免费网址| 老司机精品视频免费| 亚洲视频在线免费| 日韩免费视频一区二区| 亚洲美免无码中文字幕在线| 四虎国产精品免费久久| 亚洲变态另类一区二区三区| 免费A级毛片无码A∨男男| 久久久久久国产a免费观看不卡 | 亚洲国产成人无码AV在线影院| 日本牲交大片免费观看| 一个人看的hd免费视频| 亚洲AV无码不卡无码| 一个人免费高清在线观看| 最新亚洲人成无码网站| 337p日本欧洲亚洲大胆裸体艺术| 久久青草国产免费观看| 亚洲免费电影网站| 亚洲高清免费视频| 日本免费一区二区三区| 亚洲精品无码mⅴ在线观看| 无码精品人妻一区二区三区免费看| 亚洲视频网站在线观看| 日韩毛片无码永久免费看| 国产99视频精品免费视频76| 久久精品国产亚洲av日韩| 成年女人永久免费观看片| a毛片久久免费观看| 成人亚洲国产va天堂|