日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java 75-76

發布時間:2025/7/14 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 75-76 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?七十五、考慮使用自定義的序列化形式:?

????? 設計一個類的序列化形式和設計該類的API同樣重要,因此在沒有認真考慮好默認的序列化形式是否合適之前,不要貿然使用默認的序列化行為。在作出決定之前,你需要從靈活性、性能和正確性多個角度對這種編碼形式進行考察。一般來講,只有當你自行設計的自定義序列化形式與默認的形式基本相同時,才能接受默認的序列化形式。比如,當一個對象的物理表示法等同于它的邏輯內容,可能就適合于使用默認的序列化形式。見如下代碼示例:

  • public?class?Name?implements?Serializable?{?
  • ?????????private?final?String?lastName;?
  • ?????????private?final?String?firstName;?
  • ?????????private?final?String?middleName;?
  • ?????????...?...?
  • ?????}?
  • 從邏輯角度而言,該類的三個域字段精確的反應出它的邏輯內容。然而有的時候,即便默認的序列化形式是合適的,通常還必須提供一個readObject方法以保證約束關系和安全性,如上例代碼中,firstNamelastName不能為null等。

    ????? 下面我們再看一個極端的例子:

  • public?final?class?StringList?implements?Serializable?{?
  • ?????????private?int?size?=?0;?
  • ?????????private?Entry?head?=?null;?
  • ?????????private?static?class?Entry?implements?Serializable?{?
  • ?????????????String?data;?
  • ?????????????Entry?next;?
  • ?????????????Entry?previous;?
  • ?????????}?
  • ?????}?
  • ?對于上面的示例代碼,如果采用默認形式的序列化,將會導致雙向鏈表中的每一個節點的數據以及前后關系都會被序列化。因此這種物理表示法與它的邏輯數據內容有實質性的區別時,使用默認序列化形式會有以下幾個缺點:

    ????? 1. 它使這個類的導出API永遠的束縛在該類的內部表示法上,即使今后找到更好的的實現方式,也無法擺脫原有的實現方式。

    ????? 2. 它會消耗過多的空間。事實上對于上面的示例代碼,我們只需要序列化數據部分,可以完全忽略鏈表節點之間的關系。

    ????? 3. 它會消耗過多的時間。

    ????? 4. 它會引起棧溢出。

    ???? 根據以上四點,我們修訂了StringList類的序列化實現方式,見如下代碼:

  • public?final?class?StringList?implements?Serializable?{?
  • ?????????private?transient?int?size?=?0;?
  • ?????????private?transient?Entry?head?=?null;?
  • ?????????private?static?class?Entry?{?
  • ?????????????String?data;?
  • ?????????????Entry?next;?
  • ?????????????Entry?previous;?
  • ?????????}?
  • ?????????private?void?writeObject(ObjectOutputStream?s)?throws?IOException?{?
  • ?????????????s.defaultWriteObject();?
  • ?????????????s.writeInt(size);?
  • ?????????????for?(Entry?e?=?head;?e?!=?null;?e?=?e.next)?
  • ?????????????????s.writeObject(e.data);?
  • ?????????}?
  • ?????????private?void?readObject(ObjectInputStream?s)??
  • ?????????????throws?IOException,?ClassNotFoundException?{?
  • ?????????????s.defaultReadObject();?
  • ?????????????int?numElemnet?=?s.readInt();?
  • ?????????????for?(int?i?=?0;?i?<?numElements;?i++)?
  • ?????????????????add((String)s.readObject());?
  • ?????????}?
  • ?????????public?final?void?add(String?s)?{?...?}?
  • ?????????...?...?
  • ?????}?
  • 在修訂代碼中,所有的域字段都是transient,但writeObjectreadObject方法的首要任務仍然是先調用defaultWriteObjectdefaultReadObject方法,即便這對于缺省序列化形式并不是必須的。因為在今后的修改中,很有可能會為該類添加非transient域字段,一旦忘記同步修改writeObjectreadObject方法,將會導致序列化和反序列化的數據處理方式不一致。

    ????? 對于默認序列化還需要進一步說明的是,當一個或多個域字段被標記為transient時,如果要進行反序列化,這些域字段都將被初始化為其類型默認值,如對象引用域被置為null,數值基本域的默認值為0boolean域的默認值為false。如果這些值不能被任何transient域所接受,你就必須提供一個readObject方法。它首先調用defaultReadObject,然后再把這些transient域恢復為可接受的值。

    ????? 最后需要說明的是,無論你是否使用默認的序列化形式,如果在讀取整個對象狀態的任何其他方法上強制任何同步,則也必須在對象序列化上強制這種同步,見如下代碼:

  • private?synchronized?void?writeObject(ObjectOutputStream?s)?throws?IOException?{?
  • ?????????s.defaultWriteObject();?
  • ?????}?
  • 七十六、保護性的編寫readObject方法:

    ????? 在條目39中介紹了一個不可變的日期范圍類,它包含可變的私有Date域。該類通過在其構造器和訪問方法中保護性的拷貝Date對象,極力的維護其約束條件和不可變性。見如下代碼:

  • public?final?class?Period?{?
  • ?????????private?final?Date?start;?
  • ?????????private?final?Date?end;?
  • ?????????public?Period(Date?start,?Date?end)?{?
  • ?????????????this.start?=?new?Date(start.getTime());?
  • ?????????????this.end?=?new?Date(end.getTime());?
  • ?????????????if?(this.start.compareTo(this.end)?>?0)?
  • ?????????????????throw?new?IllegalArgumentException();?
  • ?????????}?
  • ?????????public?Date?start()?{?
  • ?????????????return?new?Date(start.getTime());?
  • ?????????}?
  • ?????????public?Date?end()?{?
  • ?????????????return?new?Date(end.getTime());?
  • ?????????}?
  • ?????????public?String?toString()?{?
  • ?????????????return?start?+?"?-?"?+?end;?
  • ?????????}?
  • ?????????...?...?
  • ?????}?
  • ?這個對象的物理表示法和其邏輯表示法完全匹配,所以我們可以使用默認的序列化形式。因此在聲明該類的地方增加" implements Serializable "。然而,如果你真是這樣做了,那么這個類將不再保證他的關鍵約束了。

    ????? 問題在于,如果反序列化的數據源來自于該類實例的正常序列化,那么將不會引發任何問題。如果恰恰相反,反序列化的數據源來自于一組偽造的數據流,事實上,反序列化的機制就是從一組有規則的數據流中實例化指定對象,那么我們將不得不面對Period實例對象的內部約束被破壞的危險,見如下代碼:

  • public?class?BogusPeriod?{?
  • ?????????private?static?final?byte[]?serializedForm?=?new?byte[]?{?
  • ?????????????...?...?//這里是一組偽造的字節流數據?
  • ?????????};?
  • ?????????public?static?void?main(String[]?args)?[?
  • ?????????????Period?p?=?(Period)deserialize(serializedForm);?
  • ?????????????System.out.println(p);?
  • ?????????}?
  • ?????????private?static?Object?deserialize(byte[]?sf)?{?
  • ?????????????try?{?
  • ?????????????????InputStream?is?=?new?ByteArrayInputStream(sf);?
  • ?????????????????ObjectInputStream?ois?=?new?ObjectInputStream(is);?
  • ?????????????????return?ois.readObject();?
  • ?????????????}?catch?(Exception?e)?{?
  • ?????????????????throw?new?IllegalArgumentException(e);?
  • ?????????????}?
  • ?????????}?
  • ?????}?
  • ?如果執行上面的代碼就會發現Period的約束被打破了,end的日期早于start。為了修正這個問題,可以為Period提供一個readObject方法,該方法首先調用defaultReadObject,然后檢查被反序列化之后的對象的有效性。如果檢查失敗,則拋出InvalidObjectException異常,使反序列化過程不能成功地完成。

  • private?void?readObject(ObjectInputStream?s)?
  • ?????????throws?IOException,ClassNotFoundException?{?
  • ?????????s.defaultReadObject();?
  • ?????????if?(start.compareTo(end)?>?0)?
  • ?????????????throw?new?InvalidObjectException(start?+?"?after?"?+?end);?
  • ?????}?
  • 如果執行上面的代碼就會發現Period的約束被打破了,end的日期早于start。為了修正這個問題,可以為Period提供一個readObject方法,該方法首先調用defaultReadObject,然后檢查被反序列化之后的對象的有效性。如果檢查失敗,則拋出InvalidObjectException異常,使反序列化過程不能成功地完成。

  • private?void?readObject(ObjectInputStream?s)?
  • ?????????throws?IOException,ClassNotFoundException?{?
  • ?????????s.defaultReadObject();?
  • ?????????if?(start.compareTo(end)?>?0)?
  • ?????????????throw?new?InvalidObjectException(start?+?"?after?"?+?end);?
  • ?????}?
  • 除了上面的***方式之外,還存在著另外一種更為隱匿的***方式,它也是通過偽造序列化數據流的方式來騙取反序列化方法的信任。它在偽造數據時,將私有域字段的引用在外部保存起來,這樣當對象實例反序列化成功后,由于外部仍然可以操作其內部數據,因此危險仍然存在。如何避免該風險呢?見如下修訂后的readObject方法:

  • private?void?readObject(ObjectInputStream?s)?
  • ?????????throws?IOException,ClassNotFoundException?{?
  • ?????????s.defaultReadObject();?
  • ?????????//執行保護性copy?
  • ?????????start?=?new?Date(start.getTime());?
  • ?????????end?=?new?Date(end.getTime());?
  • ?????????if?(start.compareTo(end)?>?0)?
  • ?????????????throw?new?InvalidObjectException(start?+?"?after?"?+?end);?
  • ?????}?
  • 注意,保護性copy一定要在有效性檢查之前進行。

    ????? 這里給出一個基本的規則,可以用來幫助確定默認的readObject方法是否可以被接受。規則是增加一個公有的構造器,其參數對應于該對象中每個非transient域,并且無論參數的值是什么,都是不進行檢查就可以保存到相應的域中的。對于這樣的做法如果仍然可以接受,那么默認的readObject就是合理,否則就需要提供一個顯式的readObject方法。

    ????? 對于非final的可序列化類,在readObject方法和構造器之間還有其他類似的地方,readObject方法不可以調用可被覆蓋的方法,無論是直接調用還是間接調都不可以。如果違反了該規則,并且覆蓋了該方法,被覆蓋的方法將在子類的狀態被反序列化之前先運行。程序很可能會失敗。

    轉載于:https://blog.51cto.com/andra/780485

    總結

    以上是生活随笔為你收集整理的java 75-76的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。