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

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

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

    @OverWrite BlogJava

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      128 隨筆 :: 0 文章 :: 29 評(píng)論 :: 0 Trackbacks

     
    默認(rèn)的序列化機(jī)制并不難操縱。然而,假若有特殊要求又該怎么辦呢?我們可能有特殊的安全問(wèn)題,不希望對(duì)象的某一部分序列化;或者某一個(gè)子對(duì)象完全不必序列化,因?yàn)閷?duì)象恢復(fù)以后,那一部分需要重新創(chuàng)建。
    此時(shí),通過(guò)實(shí)現(xiàn)Externalizable接口,用它代替Serializable接口,便可控制序列化的具體過(guò)程。這個(gè)Externalizable接口擴(kuò)展了Serializable,并增添了兩個(gè)方法:writeExternal()和readExternal()。在序列化和重新裝配的過(guò)程中,會(huì)自動(dòng)調(diào)用這兩個(gè)方法,以便我們執(zhí)行一些特殊操作。
    下面這個(gè)例子展示了Externalizable接口方法的簡(jiǎn)單應(yīng)用。注意Blip1和Blip2幾乎完全一致,除了極微小的差別(自己研究一下代碼,看看是否能發(fā)現(xiàn)):

    //: Blips.java
    // Simple use of Externalizable & a pitfall
    import java.io.*;
    import java.util.*;

    class Blip1 implements Externalizable {
      
    public Blip1() {
        System.out.println(
    "Blip1 Constructor");
      }

      
    public void writeExternal(ObjectOutput out)
          
    throws IOException {
        System.out.println(
    "Blip1.writeExternal");
      }

      
    public void readExternal(ObjectInput in)
         
    throws IOException, ClassNotFoundException {
        System.out.println(
    "Blip1.readExternal");
      }

    }


    class Blip2 implements Externalizable {
      Blip2() 
    {
        System.out.println(
    "Blip2 Constructor");
      }

      
    public void writeExternal(ObjectOutput out)
          
    throws IOException {
        System.out.println(
    "Blip2.writeExternal");
      }

      
    public void readExternal(ObjectInput in)
         
    throws IOException, ClassNotFoundException {
        System.out.println(
    "Blip2.readExternal");
      }

    }


    public class Blips {
      
    public static void main(String[] args) {
        System.out.println(
    "Constructing objects:");
        Blip1 b1 
    = new Blip1();
        Blip2 b2 
    = new Blip2();
        
    try {
          ObjectOutputStream o 
    =
            
    new ObjectOutputStream(
              
    new FileOutputStream("Blips.out"));
          System.out.println(
    "Saving objects:");
          o.writeObject(b1);
          o.writeObject(b2);
          o.close();
          
    // Now get them back:
          ObjectInputStream in =
            
    new ObjectInputStream(
              
    new FileInputStream("Blips.out"));
          System.out.println(
    "Recovering b1:");
          b1 
    = (Blip1)in.readObject();
          
    // OOPS! Throws an exception:
    //!   System.out.println("Recovering b2:");
    //!   b2 = (Blip2)in.readObject();
        }
     catch(Exception e) {
          e.printStackTrace();
        }

      }

    }
     ///:~

     

    該程序輸出如下:

    Constructing objects:
    Blip1 Constructor
    Blip2 Constructor
    Saving objects:
    Blip1.writeExternal
    Blip2.writeExternal
    Recovering b1:
    Blip1 Constructor
    Blip1.readExternal
    未恢復(fù)Blip2對(duì)象的原因是那樣做會(huì)導(dǎo)致一個(gè)違例。你找出了Blip1和Blip2之間的區(qū)別嗎?Blip1的構(gòu)建器是“公共的”(public),Blip2的構(gòu)建器則不然,這樣便會(huì)在恢復(fù)時(shí)造成違例。試試將Blip2的構(gòu)建器屬性變成“public”,然后刪除//!注釋標(biāo)記,看看是否能得到正確的結(jié)果。
    恢復(fù)b1后,會(huì)調(diào)用Blip1默認(rèn)構(gòu)建器。這與恢復(fù)一個(gè)Serializable(可序列化)對(duì)象不同。在后者的情況下,對(duì)象完全以它保存下來(lái)的二進(jìn)制位為基礎(chǔ)恢復(fù),不存在構(gòu)建器調(diào)用。而對(duì)一個(gè)Externalizable對(duì)象,所有普通的默認(rèn)構(gòu)建行為都會(huì)發(fā)生(包括在字段定義時(shí)的初始化),而且會(huì)調(diào)用readExternal()。必須注意這一事實(shí)——特別注意所有默認(rèn)的構(gòu)建行為都會(huì)進(jìn)行——否則很難在自己的Externalizable對(duì)象中產(chǎn)生正確的行為。
    下面這個(gè)例子揭示了保存和恢復(fù)一個(gè)Externalizable對(duì)象必須做的全部事情:

     

    //: Blip3.java
    // Reconstructing an externalizable object
    import java.io.*;
    import java.util.*;

    class Blip3 implements Externalizable {
      
    int i;
      String s; 
    // No initialization
      public Blip3() {
        System.out.println(
    "Blip3 Constructor");
        
    // s, i not initialized
      }

      
    public Blip3(String x, int a) {
        System.out.println(
    "Blip3(String x, int a)");
        s 
    = x;
        i 
    = a;
        
    // s & i initialized only in non-default
        
    // constructor.
      }

      
    public String toString() return s + i; }
      
    public void writeExternal(ObjectOutput out)
          
    throws IOException {
        System.out.println(
    "Blip3.writeExternal");
        
    // You must do this:
        out.writeObject(s); out.writeInt(i);
      }

      
    public void readExternal(ObjectInput in)
         
    throws IOException, ClassNotFoundException {
        System.out.println(
    "Blip3.readExternal");
        
    // You must do this:
        s = (String)in.readObject(); 
        i 
    =in.readInt();
      }

      
    public static void main(String[] args) {
        System.out.println(
    "Constructing objects:");
        Blip3 b3 
    = new Blip3("A String "47);
        System.out.println(b3.toString());
        
    try {
          ObjectOutputStream o 
    =
            
    new ObjectOutputStream(
              
    new FileOutputStream("Blip3.out"));
          System.out.println(
    "Saving object:");
          o.writeObject(b3);
          o.close();
          
    // Now get it back:
          ObjectInputStream in =
            
    new ObjectInputStream(
              
    new FileInputStream("Blip3.out"));
          System.out.println(
    "Recovering b3:");
          b3 
    = (Blip3)in.readObject();
          System.out.println(b3.toString());
        }
     catch(Exception e) {
          e.printStackTrace();
        }

      }

    }
     ///:~



    其中,字段s和i只在第二個(gè)構(gòu)建器中初始化,不關(guān)默認(rèn)構(gòu)建器的事。這意味著假如不在readExternal中初始化s和i,它們就會(huì)成為null(因?yàn)樵趯?duì)象創(chuàng)建的第一步中已將對(duì)象的存儲(chǔ)空間清除為1)。若注釋掉跟隨于“You must do this”后面的兩行代碼,并運(yùn)行程序,就會(huì)發(fā)現(xiàn)當(dāng)對(duì)象恢復(fù)以后,s是null,而i是零。
    若從一個(gè)Externalizable對(duì)象繼承,通常需要調(diào)用writeExternal()和readExternal()的基礎(chǔ)類版本,以便正確地保存和恢復(fù)基礎(chǔ)類組件。
    所以為了讓一切正常運(yùn)作起來(lái),千萬(wàn)不可僅在writeExternal()方法執(zhí)行期間寫入對(duì)象的重要數(shù)據(jù)(沒(méi)有默認(rèn)的行為可用來(lái)為一個(gè)Externalizable對(duì)象寫入所有成員對(duì)象)的,而是必須在readExternal()方法中也恢復(fù)那些數(shù)據(jù)。初次操作時(shí)可能會(huì)有些不習(xí)慣,因?yàn)镋xternalizable對(duì)象的默認(rèn)構(gòu)建行為使其看起來(lái)似乎正在進(jìn)行某種存儲(chǔ)與恢復(fù)操作。但實(shí)情并非如此。

    1. transient(臨時(shí))關(guān)鍵字
    控制序列化過(guò)程時(shí),可能有一個(gè)特定的子對(duì)象不愿讓Java的序列化機(jī)制自動(dòng)保存與恢復(fù)。一般地,若那個(gè)子對(duì)象包含了不想序列化的敏感信息(如密碼),就會(huì)面臨這種情況。即使那種信息在對(duì)象中具有“private”(私有)屬性,但一旦經(jīng)序列化處理,人們就可以通過(guò)讀取一個(gè)文件,或者攔截網(wǎng)絡(luò)傳輸?shù)玫剿?br /> 為防止對(duì)象的敏感部分被序列化,一個(gè)辦法是將自己的類實(shí)現(xiàn)為Externalizable,就象前面展示的那樣。這樣一來(lái),沒(méi)有任何東西可以自動(dòng)序列化,只能在writeExternal()明確序列化那些需要的部分。
    然而,若操作的是一個(gè)Serializable對(duì)象,所有序列化操作都會(huì)自動(dòng)進(jìn)行。為解決這個(gè)問(wèn)題,可以用transient(臨時(shí))逐個(gè)字段地關(guān)閉序列化,它的意思是“不要麻煩你(指自動(dòng)機(jī)制)保存或恢復(fù)它了——我會(huì)自己處理的”。
    例如,假設(shè)一個(gè)Login對(duì)象包含了與一個(gè)特定的登錄會(huì)話有關(guān)的信息。校驗(yàn)登錄的合法性時(shí),一般都想將數(shù)據(jù)保存下來(lái),但不包括密碼。為做到這一點(diǎn),最簡(jiǎn)單的辦法是實(shí)現(xiàn)Serializable,并將password字段設(shè)為transient。下面是具體的代碼:

     

    //: Logon.java
    // Demonstrates the "transient" keyword
    import java.io.*;
    import java.util.*;

    class Logon implements Serializable {
      
    private Date date = new Date();
      
    private String username;
      
    private transient String password;
      Logon(String name, String pwd) 
    {
        username 
    = name;
        password 
    = pwd;
      }

      
    public String toString() {
        String pwd 
    =
          (password 
    == null? "(n/a)" : password;
        
    return "logon info: \n   " +
          
    "username: " + username +
          
    "\n   date: " + date.toString() +
          
    "\n   password: " + pwd;
      }

      
    public static void main(String[] args) {
        Logon a 
    = new Logon("Hulk""myLittlePony");
        System.out.println( 
    "logon a = " + a);
        
    try {
          ObjectOutputStream o 
    =
            
    new ObjectOutputStream(
              
    new FileOutputStream("Logon.out"));
          o.writeObject(a);
          o.close();
          
    // Delay:
          int seconds = 5;
          
    long t = System.currentTimeMillis()
                 
    + seconds * 1000;
          
    while(System.currentTimeMillis() < t)
            ;
          
    // Now get them back:
          ObjectInputStream in =
            
    new ObjectInputStream(
              
    new FileInputStream("Logon.out"));
          System.out.println(
            
    "Recovering object at " + new Date());
          a 
    = (Logon)in.readObject();
          System.out.println( 
    "logon a = " + a);
        }
     catch(Exception e) {
          e.printStackTrace();
        }

      }

    }
     ///:~



    可以看到,其中的date和username字段保持原始狀態(tài)(未設(shè)成transient),所以會(huì)自動(dòng)序列化。然而,password被設(shè)為transient,所以不會(huì)自動(dòng)保存到磁盤;另外,自動(dòng)序列化機(jī)制也不會(huì)作恢復(fù)它的嘗試。輸出如下:

    logon a = logon info:
       username: Hulk
       date: Sun Mar 23 18:25:53 PST 1997
       password: myLittlePony
    Recovering object at Sun Mar 23 18:25:59 PST 1997
    logon a = logon info:
       username: Hulk
       date: Sun Mar 23 18:25:53 PST 1997
       password: (n/a)一旦對(duì)象恢復(fù)成原來(lái)的樣子,password字段就會(huì)變成null。注意必須用toString()檢查password是否為null,因?yàn)槿粲眠^(guò)載的“+”運(yùn)算符來(lái)裝配一個(gè)String對(duì)象,而且那個(gè)運(yùn)算符遇到一個(gè)null句柄,就會(huì)造成一個(gè)名為NullPointerException的違例(新版Java可能會(huì)提供避免這個(gè)問(wèn)題的代碼)。
    我們也發(fā)現(xiàn)date字段被保存到磁盤,并從磁盤恢復(fù),沒(méi)有重新生成。
    由于Externalizable對(duì)象默認(rèn)時(shí)不保存它的任何字段,所以transient關(guān)鍵字只能伴隨Serializable使用。

    2. Externalizable的替代方法
    若不是特別在意要實(shí)現(xiàn)Externalizable接口,還有另一種方法可供選用。我們可以實(shí)現(xiàn)Serializable接口,并添加(注意是“添加”,而非“覆蓋”或者“實(shí)現(xiàn)”)名為writeObject()和readObject()的方法。一旦對(duì)象被序列化或者重新裝配,就會(huì)分別調(diào)用那兩個(gè)方法。也就是說(shuō),只要提供了這兩個(gè)方法,就會(huì)優(yōu)先使用它們,而不考慮默認(rèn)的序列化機(jī)制。
    這些方法必須含有下列準(zhǔn)確的簽名:

    private void
      writeObject(ObjectOutputStream stream)
        throws IOException;

    private void
      readObject(ObjectInputStream stream)
        throws IOException, ClassNotFoundException從設(shè)計(jì)的角度出發(fā),情況變得有些撲朔迷離。首先,大家可能認(rèn)為這些方法不屬于基礎(chǔ)類或者Serializable接口的一部分,它們應(yīng)該在自己的接口中得到定義。但請(qǐng)注意它們被定義成“private”,這意味著它們只能由這個(gè)類的其他成員調(diào)用。然而,我們實(shí)際并不從這個(gè)類的其他成員中調(diào)用它們,而是由ObjectOutputStream和ObjectInputStream的writeObject()及readObject()方法來(lái)調(diào)用我們對(duì)象的writeObject()和readObject()方法(注意我在這里用了很大的抑制力來(lái)避免使用相同的方法名——因?yàn)榕禄煜4蠹铱赡芷婀諳bjectOutputStream和ObjectInputStream如何有權(quán)訪問(wèn)我們的類的private方法——只能認(rèn)為這是序列化機(jī)制玩的一個(gè)把戲。
    在任何情況下,接口中的定義的任何東西都會(huì)自動(dòng)具有public屬性,所以假若writeObject()和readObject()必須為private,那么它們不能成為接口(interface)的一部分。但由于我們準(zhǔn)確地加上了簽名,所以最終的效果實(shí)際與實(shí)現(xiàn)一個(gè)接口是相同的。
    看起來(lái)似乎我們調(diào)用ObjectOutputStream.writeObject()的時(shí)候,我們傳遞給它的Serializable對(duì)象似乎會(huì)被檢查是否實(shí)現(xiàn)了自己的writeObject()。若答案是肯定的是,便會(huì)跳過(guò)常規(guī)的序列化過(guò)程,并調(diào)用writeObject()。readObject()也會(huì)遇到同樣的情況。
    還存在另一個(gè)問(wèn)題。在我們的writeObject()內(nèi)部,可以調(diào)用defaultWriteObject(),從而決定采取默認(rèn)的writeObject()行動(dòng)。類似地,在readObject()內(nèi)部,可以調(diào)用defaultReadObject()。下面這個(gè)簡(jiǎn)單的例子演示了如何對(duì)一個(gè)Serializable對(duì)象的存儲(chǔ)與恢復(fù)進(jìn)行控制:


     

    //: SerialCtl.java
    // Controlling serialization by adding your own
    // writeObject() and readObject() methods.
    import java.io.*;

    public class SerialCtl implements Serializable {
      String a;
      
    transient String b;
      
    public SerialCtl(String aa, String bb) {
        a 
    = "Not Transient: " + aa;
        b 
    = "Transient: " + bb;
      }

      
    public String toString() {
        
    return a + "\n" + b;
      }

      
    private void 
        writeObject(ObjectOutputStream stream)
          
    throws IOException {
        stream.defaultWriteObject();
        stream.writeObject(b);
      }

      
    private void 
        readObject(ObjectInputStream stream)
          
    throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        b 
    = (String)stream.readObject();
      }

      
    public static void main(String[] args) {
        SerialCtl sc 
    = 
          
    new SerialCtl("Test1""Test2");
        System.out.println(
    "Before:\n" + sc);
        ByteArrayOutputStream buf 
    = 
          
    new ByteArrayOutputStream();
        
    try {
          ObjectOutputStream o 
    =
            
    new ObjectOutputStream(buf);
          o.writeObject(sc);
          
    // Now get it back:
          ObjectInputStream in =
            
    new ObjectInputStream(
              
    new ByteArrayInputStream(
                buf.toByteArray()));
          SerialCtl sc2 
    = (SerialCtl)in.readObject();
          System.out.println(
    "After:\n" + sc2);
        }
     catch(Exception e) {
          e.printStackTrace();
        }

      }

    }
     ///:~


    在這個(gè)例子中,一個(gè)String保持原始狀態(tài),其他設(shè)為transient(臨時(shí)),以便證明非臨時(shí)字段會(huì)被defaultWriteObject()方法自動(dòng)保存,而transient字段必須在程序中明確保存和恢復(fù)。字段是在構(gòu)建器內(nèi)部初始化的,而不是在定義的時(shí)候,這證明了它們不會(huì)在重新裝配的時(shí)候被某些自動(dòng)化機(jī)制初始化。
    若準(zhǔn)備通過(guò)默認(rèn)機(jī)制寫入對(duì)象的非transient部分,那么必須調(diào)用defaultWriteObject(),令其作為writeObject()中的第一個(gè)操作;并調(diào)用defaultReadObject(),令其作為readObject()的第一個(gè)操作。這些都是不常見(jiàn)的調(diào)用方法。舉個(gè)例子來(lái)說(shuō),當(dāng)我們?yōu)橐粋€(gè)ObjectOutputStream調(diào)用defaultWriteObject()的時(shí)候,而且沒(méi)有為其傳遞參數(shù),就需要采取這種操作,使其知道對(duì)象的句柄以及如何寫入所有非transient的部分。這種做法非常不便。
    transient對(duì)象的存儲(chǔ)與恢復(fù)采用了我們更熟悉的代碼。現(xiàn)在考慮一下會(huì)發(fā)生一些什么事情。在main()中會(huì)創(chuàng)建一個(gè)SerialCtl對(duì)象,隨后會(huì)序列化到一個(gè)ObjectOutputStream里(注意這種情況下使用的是一個(gè)緩沖區(qū),而非文件——與ObjectOutputStream完全一致)。正式的序列化操作是在下面這行代碼里發(fā)生的:
    o.writeObject(sc);
    其中,writeObject()方法必須核查sc,判斷它是否有自己的writeObject()方法(不是檢查它的接口——它根本就沒(méi)有,也不是檢查類的類型,而是利用反射方法實(shí)際搜索方法)。若答案是肯定的,就使用那個(gè)方法。類似的情況也會(huì)在readObject()上發(fā)生。或許這是解決問(wèn)題唯一實(shí)際的方法,但確實(shí)顯得有些古怪。

    3. 版本問(wèn)題
    有時(shí)候可能想改變一個(gè)可序列化的類的版本(比如原始類的對(duì)象可能保存在數(shù)據(jù)庫(kù)中)。盡管這種做法得到了支持,但一般只應(yīng)在非常特殊的情況下才用它。此外,它要求操作者對(duì)背后的原理有一個(gè)比較深的認(rèn)識(shí),而我們?cè)谶@里還不想達(dá)到這種深度。JDK 1.1的HTML文檔對(duì)這一主題進(jìn)行了非常全面的論述(可從Sun公司下載,但可能也成了Java開(kāi)發(fā)包聯(lián)機(jī)文檔的一部分)。

    posted on 2008-05-28 12:00 vesung 閱讀(678) 評(píng)論(0)  編輯  收藏 所屬分類: Java
    主站蜘蛛池模板: 中文字幕亚洲综合久久2| 亚洲综合久久综合激情久久| 91亚洲一区二区在线观看不卡| 中国亚洲呦女专区| 毛片基地看看成人免费| 无遮免费网站在线入口| 亚洲AV无码乱码精品国产| 久久精品国产亚洲AV高清热| 男性gay黄免费网站| 99re在线精品视频免费| 国产美女精品视频免费观看| 久久亚洲精品成人| 国产成人高清亚洲一区久久| 曰批全过程免费视频网址| 亚洲av无码不卡私人影院| 亚洲图片中文字幕| 成人自慰女黄网站免费大全| 在线免费观看一级毛片| 久久综合九九亚洲一区| 爱情岛亚洲论坛在线观看| 无码日韩精品一区二区三区免费| 全部免费毛片在线| 亚洲人成综合在线播放| av永久免费网站在线观看| 四只虎免费永久观看| 亚洲av无码一区二区三区天堂古代| 国产午夜精品理论片免费观看 | 免费三级毛片电影片| 亚洲精品无码成人片久久| 在线亚洲精品视频| 91免费精品国自产拍在线不卡| 亚洲AV无码精品色午夜果冻不卡 | 日韩视频免费在线观看| 亚洲成A人片在线观看中文| 免费黄色网址入口| 亚洲欧洲日韩在线电影| 久久免费美女视频| 久久久久久亚洲精品不卡| 美女被艹免费视频| 青青青青青青久久久免费观看| 亚洲av无码片在线观看|