《Effective Java2》笔录
轉自:http://www.verydemo.com/demo_c89_i134810.html
Joshua Bloch在國內出版的書包括《Effective Java2》《Java Puzzlers》《Java Concurrency in Practive》(合著)。特別是這本《Effective Java2》,每次重讀都有新的收獲,本文將過去和現在記錄的一些讀書筆記,感想整理了一下。
?(標記說明∷表示筆記內容 ※為補充內容)
?
第1章 引言(略)
第2章 創建和銷毀對象
第1條:考慮用靜態工廠方法代替構造器
? ∷典型的方法名有:valueOf,newInstance,getInstance等,特別適用于,服務提供者框架上使用。
??? 服務提供者框架又包含三個重要組件:服務接口(Connection),提供者注冊 API(DriverManager.registerDriver),服務訪問API(DriverManager.getConnection),一 個可選:服務提供者接口(Driver)
例:
Class.forName("oracle.jdbc.driver.OracleDriver");
String dbUrl = "jdbc:oracle:thin:@127.0.0.1 :1521:orcl";
Connection conn=DriverManager.getConnection(url,user,password);
Statement stmt = conn.createStatement(...);
? ※實現技巧:在OracleDriver的代碼中有一個類級的static塊代碼,其中包含如下代碼,即每次Class.forName()加載類后自動完成注冊。
DriverManager.registerDriver(new oracle.jdbc.OracleDriver());
第2條:遇到多個構造器參數時要考慮用構建器
? ∷當參數變多并且使用方式多樣時,普通的構造器易用性會比較差(參數位置易錯)。這時Builder模式應該是比較好的選擇。(優雅的解決方法總是要多記一下)
???? 例子:
public class NutritionFacts{
? private final int servingSize;
? private final int calories;
? ..
? public static class Builder{
?
??? // Required parameters
??? private final int servingSize;
??? ..
??? // Optional parameters - initialized to default values
??? private int calories =0;
??? ..
??? public Builder( int servingSize, int servings ){
????? this.servingSize = servingSize;
????? ..
??? }
??? public Bilder calories(int val)
????? { calories = val; retrun this; }
??? ..
??? public NutritionFacts build(){
????? return new NutritionFacts(this);
??? }
?
}
//使用例
Facts cocaCola = new Facts.Builter(240,8).calories(100)...build();
?
第3條:用私有構造器或者枚舉類型強化Singleton屬性
? ∷單元素的枚舉類型是實現 單例的最佳方式(絕對防止多次實例化,自帶序列化),其次是私有構造器(靜態公有域和靜態公有
方法兩種)
? ∷Singleton類做序列化時,為保證唯一性,必須聲明所有實例域為瞬時(transient),并使用readResolve方法
例:
// Singleton with public final field
public class Elvis {
? public static final Elvis INSTANCE = new Elvis():
? private Elvis() { ... }
? ..
? public void leaveTheBuilding() { .. }
}
// Singleton with static factory
public class Elvis {
? private static final Elvis INSTANCE = new Elvis():
? private Elvis() { ... }
? public static Elvis getInstance() { return INSTANCE; }
? ..
? public void leaveTheBuilding() { .. }
}
// Enum singleton - the preferred approach
public enum Elvis {
? INSTANCE;
? public void leaveTheBuilding() { .. }
}
第4條:通過私有構造器強化不可實例化的能力
? ∷不希望實例化的工具類必須選擇,副作用 是不會有子類
?
第5條:避免創建不必要的對象
? ∷與性能關系甚大。字符串拼接使用StringBulider,盡量使用常量static,避免自動裝箱等。
第6條:消除過期的對象引用
? ∷JAVA內存泄露是比較隱蔽的,主要有堆棧,已廢止的指針未清空,消除過期引用
? ∷緩存考慮使用WeakHashMap,或java.lang.ref。定時清理使用ScheduledThreadPoolExecutor,LinkedHashMap的removeEldestEntry()方法可移除其最舊的條目
? ∷監聽器和回調,考慮使用弱引用(weak reference)如WeakHashMap
?
第7條:避免使用終結函數
? ∷不可預測,也是沒有必要的一個方法
? ∷兩種合法用途:充當安全網和本地方法回收(從來沒用過啊)
?
第3章 對于所有對象都通用的方法
? ∷Java是單根系統,所以全體對象都要考慮Object暴露的方法(equals,hashCode,toString,clone,finalize)是否需要重寫
? ∷否則的話,無法避免使用其他類庫時,會不會發生問題。比如不重寫equals,很多集合類庫方法會出問題。
?
第8條:改寫equals時請遵守通用約定
? ∷一個完善的equals方法要考慮自反性,對稱性,傳遞性,一致性,特別是域為對象時,前面概念還要“遞歸進去”。
? ∷千萬注意不要寫成 public boolean equals(Myclass o)就悲劇了
? ※其實自己寫很是麻煩的,考慮細節不少。根據不重復造輪子的原則,“聰明人”都用Apache Commons節省時間。包括下面的hashCode和toString同樣適用,當然使用前研修一下里面的代碼就更好了。
? ※從邏輯意義上,equals的間接地表明所有數據型對象必須要有key。類型和key能夠唯一判定一個對象。常常在想,SUN當初怎么不弄一個Key關鍵字作為語言要素呢
?
第9條:改寫equals時總要改寫hashCode
? ∷hashCode涉及到集合類中為提高性能寫的代碼,如果hashCode不被正確地重寫,很多集合類方法會出錯。
?
第10條:始終要改寫toString
? ∷應該作為一個良好的編程保持,一般將該對象的“KEY”內容格式化文字串
? ※主要應用在還是用Apache Commons吧
?
第11條:謹慎地改寫clone
? ∷克隆主要關注的是可變類的復制(基本型和不可變型不需特別考慮)。當克隆對象含有數組,List等可迭代對象時,必須深入
到該結構中逐個克隆。
? ∷特別地Cloneable是一個標記接口,JDK1.6以后允許clone返回非Object對象(SUN公司給了特別通行證?)
@Override
public PhoneNumber clone(){
? try{
??? return (PhoneNumber)super.clone();
? } catch(CloneNotSupportedException e){
??? throw new AssertionError();
? }
}
? ※根據接口描述如果在沒有實現 Cloneable 接口的實例上調用 Object 的 clone 方法,則會導致拋出
CloneNotSupportedException 異常。(又是一個挺奇怪的“約定”)
?
第12條:考慮實現Comparable接口
? ∷專為 Arrays.sort()排序而用,重點注意要與equals不矛盾(還是那句話懶人都用Apache Commons)
?
第4章 類和接口
第13條:使類和成員的可訪問性最小化
? ∷為使訪問級別(private,protected,public,包級)不會濫用一個簡單方法是把說有成員都設為private,再逐步根據需要調整。
? ∷提放暴露數組,LIST等可從外部修改的變量,影響安全性(可用 Collections.unmodifiablelist做保護)
?
第14條:在公有類中使用訪問方法而非公有域
? ∷JAVA Bean的典型特性
? ×(反例)java.awt中 Point和Dimension類 屬性錯誤地使用public
?
第15條:使非可變性最小化
? ∷盡可能使用final
? ※方便的IDE工具容易生成出不該有的get,set方法,做這一步的時候需要慢一點再慢一點
?
第16條:復合優先于繼承
? ∷繼承雖然在重用方面效果顯著,卻有一些缺點:打破封裝性,無法避免超類的“意外”擴展等。只有兩者關系完全是“is-a”,即邏輯包含關系才可用繼承。Java平臺反例:Stack不應擴展Vector,Properties不應擴展Hashtable。
? ※實際項目中,繼承類往往都是可高度重用的部分,應該都有底層開發人員或架構師統一考慮
?
第17條:要么為繼承而設計,并提供文檔說明,要么就禁止繼承
? ∷強化了上一條,大多數繼承都是精心策劃為提高復用性而來。測試方法是編寫子類,經驗表明3個子類較合適。
??? 一般性規則:構造器不可調用被覆蓋的方法。
?
第18條:接口優于抽象類
? ∷相對于抽象類只能繼承一個,接口可以有任意中組合,靈活性更強。
? ※JAVA的面向接口編程毋庸置疑所以這兩個本不是一個“量級”的東西,不過抽象類在設計模式還是有用武之地的。
?
第19條:接口只用于定義類型
? ∷在接口中干任何別的事情(如定義寫常量等)都會顯得過于另類,不宜嘗試
?
第20條:類層次優于標簽類
? ※個人認為這個屬于設計問題,一個類的雜合了太多東西肯定要重新設計或重構了
?
第21條:用函數對象表示策略
? ※與設計模式相關的條目,對java.util包里的 Arrays.sort(T[] a, Comparator<? super T> c) 及函數指針做了闡述。
?
第22條:優先考慮靜態成員類
? ∷四種嵌套類
??? 靜態成員類(只有一個實例,比較節省資源),
??? 非靜態成員類(隱含外圍實例,典型用法如Adapter),
??? 匿名類(當且僅當在非靜態環境中定義,才有外部實例。不能包含靜態成員。典型用法創建函數對象如Compatator),
??? 局部類(類匿名類,沒有典型用法)
?
第5章 泛型
第23條:請不要在新代碼中使用原生態類型
? ∷既然可以享受類型安全和消除類型強制轉換的好處,沒有理由不使用泛型。
? ∷兩個例外:類文字必須使用原生態類型,關鍵字instanceof 可以也使用原生態
?
第24條:消除非受檢警告
? ∷良好的編程習慣,并且竟可能小范圍地使用@SuppressWarnings("unchecked"),同時加注釋說明。
?
第25條:列表優先于數組
? ∷原因是數組的協變性(即類型檢查在運行期做)與泛型相違,不宜一起使用。
? ∷用LIST<E>代替E[] 可能會損失一些性能或簡潔性,但是換回了更高的類型安全性(編譯器報錯)和互用性。
?
第26條:優先考慮泛型
? ※復用性增強的利器。實際項目中往往是做底層類和框架類考慮較多。
?
第27條:優先考慮泛型方法
? ∷泛型單例工廠(generic singleton factory)
例:
public interface UnaryFunction<T>{
? T apply(T arg);
}
// Generic singleton factory pattern
private static UnaryFunction<Object> IDENTITY_FUNCTION =
? new UnaryFunction<Object>(){
??? public Object apply(Object ary) { return arg; }
? }
@SuppressWarnings("unchecked")
public static <T> UnaryFunction<T> identityFunction(){
? return (UnaryFunction<T>) IDENTITY_FUNCTION;
}
public static void main(String[] args){
? String[] strings ={"jute","hemp","nylon"};
? UnaryFunction<String> sameString = identytyFunction();
? for (String s : strings)
??? System.out.println(sameString.apply(s));
? Number[] numbers ={1, 2.0, 3L};
? UnaryFunction<Number> sameNumber = identytyFunction();
? for (Number n : numbers)
??? System.out.println(sameNumber.apply(n));
}
? ※讀泛型的程序特別是好的程序是一件非常有趣的事情,同時對提高編程能力又很有幫助,比如在JDK的集合框架中就有很多。
?
第28條:利用有限制通配符來提升API的靈活性
? ∷PECS(producer-extends,consumer-super) 生產者extends 消費者super(Comparable是典型的消費者)
? ∷類型推導有相當的復雜性(※本條似乎還不夠深入)
例:顯示類型參數
public static <E> Set<E> union(Set<? extends E> s1,Set<? extends E> s2)
Set<Integer> integers = ...
Set<Double> doubles = ...
Set<Number> numbers = Union.<Number>union(integers, doubles );
? ∷類型參數和通配符之間具有雙重性,即以下兩種聲明皆可。
例:
public static <E> void swap(List<E> list, int i, int j);
public static void swap(List<?> list,int i, int j);
?=>雖然第二種更簡單,但是開發時它還需要借助第一種作為輔助類才能編譯。
public static void swap(List<?> list,int i, int j){
? swapHelper(list, i, j);
}
private static <E> void swapHelper(List<E> list,int i, int j){
? list.set(i, list.set(j, list.get(i)));
}
?
第29條:優先考慮類型安全的異構容器
? ∷以下的類所有鍵可是不同類型,并且類型安全。
? ∷局限性不能用在不可具體化類上(如:List<String>)
例:
public class Favorites{
? private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
? public <T> void putFavorite((Class<T> type, T instance){
??? if (type == null)
????? throw new NullPointerException("Type is null");
??? favorites.put(type, type.cast(instance));
? }
? public <T> T getFavorite(Class<T> type){
??? return type.cast(favorites.get(type));
? }
}
?
第6章 枚舉和注解
? ※本章的enum的例子有很強參考價值。內容非常精彩,所以條目不做總結,看書就好了,幾乎可以知曉enum能做的一切。
? ※enum內部可用抽象方法,外部可用接口實現,特別是還有EnumSet(代替位域,簡單地說即多重選項),EnumMap(讓分類代碼更完美)協助。只有一個限制,無法再繼承。
第30條:用enum代替int常量
第31條:用實例域代替序數
第32條:用EnumSet代替位域
第33條:用EnumMap代替序數索引
第34條:用接口模擬可伸縮的枚舉
? ※是不錯,不過一直沒實際應用過
第35條:注解優先于命名模式
? ∷這里基本是在講JUnit的發展史,JUnit3到JUnit4的主要實現區別。
第36條:堅持使用Override注解
? ※特別是equals上,簡單有效防止誤用
第37條:用標記接口定義類型
? ∷標記接口是沒有包含方法聲明的借口(如:Serializable)。
? ∷如果標記是應用到任何程序元素而不是類或接口,就必須用注解。如果標記只應用給類和接口,并且還要編寫一個或多個只接受這種標記的方法就用標記接口,反之如果要永遠限制這個標記只用于特殊接口的元素就使用改接口的子接口,都不是的場合用注解。
? ※這一條有點難,各人領悟
?
第7章 方法
第38條:檢查參數的有效性
? ∷應提前構想方法的參數合法性,并反映到文檔中,對盡可能對內部函數使用 Assert。
?
第39條:必要時進行保護性拷貝
? ∷與安全性相關的條目,當構造器參數為可變對象時,應復制后,對復制對象進行檢查及后續操作
? ∷內部組件被返回給客戶端之前,同樣道理也應該做保護性拷貝。
?
第40條:謹慎設計方法簽名
? ∷謹慎地選擇方法的名稱。不要過于追求提供便利的方法。避免過長的參數列表。
? ※貌似簡單卻見功力的一條,項目中方法名應當有詳細的約定
?
第41條:慎用重載
? ∷特別注意參數有繼承關系的方法,會導致隱晦的結果。編譯期對類型的判定,不會再運行期改變。
? ×反例 String類導出兩個重載的靜態工廠方法: value(char[])和valueOf(Object)當他們被傳遞了同樣的對象引用時,所做的事情完全不同。易引起混亂。
?
第42條:慎用可變參數
? ∷使用場合僅僅限于非常單一功能(如打印)
?
第43條:返回零長度的數組或者集合,而不是null
? ∷這個如果作為項目組一開始的約定可以節省很多代碼,注意一些共通空集的常量定義必不可少。
第44條:為所有導出的API元素編寫文檔注釋
? ∷優秀coder的必要條件
?
第8章 通用程序設計
第45條:將局部變量的作用域最小化
? ∷JAVA特色,也是面向對象語言的共性。變量聲明盡量延后,用的時候現聲明,現初始化。
第46條:for-each循環優先于傳統的for循環
? ※這條與給懶人用得,和43條共同使用效果更佳
第47條:了解和使用類庫
? ※哎,在所有里面最“廢話”的一條
?
第48條:如果需要精確的答案,請避免使用float和double
? ※看到float和double就看到誤差,簡單1+1都算不對
第49條:原語類型優先于裝箱的原語類型
? ∷主要是基于效率考慮,能不用對象時,就不要寫Integr,Long等
?
?
第50條:如果其他類型更適合,則盡量避免使用字符串
? ∷筆者的意思是數據需要精確表現時,一定不會是字符串。換句話說,字符串使用于那些不太重要的項目。
?
?
第51條:了解字符串連接的性能
? ∷又是一個與性能相關的條目,拼字符串要用StringBuilder 性能高很多
?
?
第52條:通過接口引用對象
? ∷增強靈活性的法寶
?
第53條:接口優先于反射機制
? ∷接口應該是業務級的,反射是架構級的,應該予以區分。
?
第54條:謹慎地使用本地方法
? ∷當然是能不用就不用了。
第55條:謹慎地進行優化
? ∷重構應該隨時進行,但是不是性能出現問題,不要太刻意去優化(存在風險不小)。設計和算法選擇比優化更為重要。
?
第56條:遵守普遍接受的命名慣例
? ※coder等級越高越會遵守約定,好處也很明顯,代碼可讀性更好
?
第9章 異常
第57條:只針對異常的條件才使用異常
? ∷使用異常來實現某種正常邏輯是得不償失的。
? ※一般來講正常與異常是完全并行的兩條線路,這里Bloch委婉地再次重申良好的實現一定是“笨拙地”,即投機取巧的“創新代碼”往往維護性差而走向反面。
?
第58條:對可恢復的條件使用受檢異常,對編程錯誤使用運行時異常
? ∷難點在于如何判定是否是可恢復的,需要根據具體情況分析。
第59條:避免不必要地使用受檢的異常
? ∷受檢的異常一定會給使用者造成負擔,應慎重考慮是否這樣做。(還可考慮狀態測試方法,非并發場合)
?
第60條:盡量使用標準的異常
? ∷Jdk中的異常的往往看起來更標準更易理解,經驗者會把最常用的記在可查閱的地方。
?
第61條:拋出與抽象相對應的異常
? ∷本節涉及到異常轉義,如果底層信息有保留價值則使用異常鏈(構造器參數為底層異常)
?
第62條:每個方法拋出的所有異常都要有文檔
? ∷又一條與文檔相關的良好習慣: 檢查受檢異常的@throws是否遺漏
?
第63條:在細節消息中包含失敗-捕獲信息
? ∷拋出異常時要考慮,看的人是否容易理解,同時也需要再異常構造器中設置足夠的參數。
?
第64條:努力使失敗保持原子性
? ∷比較考驗設計功力,盡量將改變狀態的部分放到異常可能出現之后的地方。異常后,保持可恢復狀態。
? ∷一般性策略:充分的參數檢查,緩存中間結果一次性提交。對于并發本分一般是不可恢復,就不需考慮了。
?
第65條:不要忽略異常
? ∷catch后別忘了處理,后果很嚴重
?
第10章 并發
? ※由于篇幅有限作者多次強調,更詳細的內容參閱《Java Concurrency in Practive》。但是這幾條內容少而精,還是非常值得留意的。
第66條:同步訪問共享的可變數據
? ∷縮小同步代碼范圍有一些技巧:壓縮共享可變數據的代碼范圍,計數使用AtomicXXX類,共享狀態使用volatile變量
?
第67條:避免過多同步
? ∷同步代碼中包含可能被客戶端覆蓋的方法,對應方法有:使用CopyOnWriteArrayList,給列表做快照
? ×反例 String是不可變對象,無需同步,StringBuffer卻內部做同步,性能變差。一般使用 StringBuilder代替。
?
第68條:executor和task優先于線程
? ∷線程池框架的選擇:
???? 小程序考慮 Exectutors.newCachedThreadPool
???? 大負載使用Exectutors.newFixedThreadPool
???? 帶定時功能使用ScheduledThreadPoolExecutor
?
第69條:并發工具優先于wait和notify
? ∷直接使用Thread如同直接使用匯編語言一樣,java.util.concurrent才是“高級語言”。
? ∷并發集合性能更佳。ConcurrentHashMap 比起Collections.synchronizedMap和Hashtable性能更佳。
? ∷同步器各有擅長的場合:
???? CountDownLatch 倒計數鎖存器,允許一個或多個線程等待一個或多個其他線程來作某些事情。
???? Semaphore 一個計數信號量,通常用于限制可以訪問某些資源(物理或邏輯的)的線程數目。
???? CyclicBarrier 允許一組線程互相等待,直到到達某個公共屏障點。
???? Exchanger 可以在對中對元素進行配對和交換的線程的同步點。
? ∷System.nanoTime 比起System.currentTimeMillis更準確更精確,且不受系統的實時時鐘的調整所影響。
? ∷如果用Thread,notifyAll較notify 保險一些。
?
第70條:線程安全性的文檔化
? ∷線程安全級別有以下幾種
???? 不可變的(immutable)--不需要同步 如String,Long,BigInteger
???? 無條件的線程安全(unconditionally thread-safe)--可變但是內部安全充分 如Random,ConcurrentHashMap
???? 有條件的線程安全(conditionally thread-safe)-- 如:Collenctions.sysnchronized集合的迭代器要求外部同步
???? 非線程安全(no thread-safe)--如:ArrayList,HashMap
???? 線程對立的(thread-hostile)--即使外部同步還是不能安全并發。非常少見,如:System.runFinalizersOnExit
? ∷鎖對象應當封裝在同步對象內部,防止外部的拒絕服務攻擊
?
第71條:慎用延遲初始化
? ∷對于實例域,使用雙重檢查模式,對于靜態域使用lazy initialization holder calss idiom
例:
// lazy initialization holder calss idiom
private static class FieldHolder{
? static final FieldType field = computeFieldValue();
}
static FieldType getField(){ return FieldHolder.field; }
?
// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;
FieldType getField(){
? FieldType result = field;
? if ( result == null ){ // First check(no locking)
??? synchronized(this){
????? result = field;
????? if (result == null)? // Second check(with locking)
??????? field = result = computeFieldValue();
??? }
? }
}
?
第72條:不要依賴于線程調度器
? ∷任何依賴于線程調度器來達到正確性或者性能要求的程序,很有可能都是不可移植的。
? ∷最好確保可運行線程的平均數量不明顯多于處理器的數量。
? ∷如果線程沒有在做有意義的工作,就該sleep。忙-等狀態會使CUP效率低下。
? ∷Thread.yield沒有可測試的語義。唯一的用途是在測試期間人為地增加程序的并發性。
?
第73條:避免使用線程組
? ∷即ThreadGroup,基本無用的類
?
第11章 序列化
? ※序列化最棘手的問題是考慮安全性問題,應為任何攻擊者都可能會篡改這個序列。
? ※java.io.Serializable 接口的特殊方法:(個人認為這部分有點不規范)
//在序列化和反序列化過程中需要特殊處理的類必須使用下列準確簽名來實現特殊方法
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;
//在從流中讀取類的一個實例時需要指定替代的類應使用的準確簽名來實現此特殊方法
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
//發送者的類的版本號(版本不同可導致InvalidClassException) 這個eclipse的提示下,大多數人都知道
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
?
第74條:謹慎地實現Serializable
? ∷實現Serializable接口后的代價:類中私有級和包級的變量都編程導出API的一部分,違反了第13條。版本變更時,為使客戶端兼容,必須要設 計一種高質量的序列化形式(見第75,78條)。序列化機制是一種語言外的對象創建機制,所以要考慮由真正的構造器建立起來的約束關系。測試成本提高。
? ∷內部類不應該實現Serializable。然而,靜態成員類卻可以實現Serializable接口。
?
第75條:考慮使用自定義的序列化形式
? ∷默認的序列化有幾個缺點:它使這個類的導出API永遠地束縛在該類的內部表現法上,消耗過多的空間和時間,還會引起棧溢出。
? ∷注意如果讀取對象的方法上有被同步的方法,必須在對象序列化上加同步。
?
第76條:保護性地編寫readObject方法
? ∷readObject相當于一個公有的構造器,以下幾條指導方針
?????對于對象引用域必須保持為私有的類,要保護性地拷貝這些域中的對象。不可變類的可變組件就屬于這一類。
????? 對于任何約束條件,如果檢查失敗,則拋出一個InvalidObjectException異常。這些檢查動作應該跟所在的保護性拷貝之后。
?????如果這個對象圖在被反序列化之后必須進行驗證,就應該使用ObjectInputValidation接口
?????無論是直接方式還是間接方式,都不要調用類中任何可被覆蓋的方法。
? ∷不要使用ObjectOutputString中的writeUnshared和readUnshared方法,不能提供必要的安全保護
?
第77條:對于實例控制,枚舉類型優先于readResolve
? ∷應盡可能使用枚舉類型來實施實例控制的約束條件。不行的話,需要實現readResolve方法,并確保該類的所有實力域都為基本型或是transient的。
?
第78條:考慮用序列化代理代替序列化實例
? ∷使用場合 必須在一個不能背客戶端擴展的類上編寫readObject或者writeObject方法的時候
? ∷實現方法:首先,為可序列話的類設計一個私有的靜態嵌套類(即序列化代理),精確地表示外圍類的實例的邏輯狀態。這個類應該有一個單獨的構造器,其參數類型就是外圍類。并和外圍類同時聲明實現Serializable 接口。
? ∷優點:不必顯示地執行有效性檢查。允許反序列化類實例有與原始序列化實例不同的類,如EnumSet。
? ∷局限:它不能與可以被客戶端擴展的類兼容。另外性能稍遜。
例:
private static class SerializationProxy implements Serializable{
? private final Date start;
? private final Date end;
? SerializationProxy(Period p){
??? this.start = p.start;
??? this.end = p.end;
? }
? private static final long serialVersionUID = ...;
}
private Object writeReplace(){
? return new SerializationProxy(this);
}
?
// 防止攻擊者偽造序列化系統
private void readObject(ObjectInputStream stream) throws InvalidObjectWxception {
? throw new InvalidObjectException("Proxy required");
}
轉載于:https://my.oschina.net/liangzhenghui/blog/178194
總結
以上是生活随笔為你收集整理的《Effective Java2》笔录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 环形链表||(Leetcode第142题
- 下一篇: 定位到元素后获取其属性_(11)让“盒子