摘要:第二章創建和銷毀對象何時以及如何創建對象,何時以及如何避免創建對象,如何確保他們能夠適時地銷毀,以及如何管理對象銷毀之前必須進行的各種清理動作。表示工廠方法所返回的對象類型。
第二章 創建和銷毀對象
何時以及如何創建對象,何時以及如何避免創建對象,如何確保他們能夠適時地銷毀,以及如何管理對象銷毀之前必須進行的各種清理動作。1 考慮用靜態工廠方法代替構造器
一般在某處獲取一個類的實例最常用的方法是提供一個共有的構造器,還有一種方法,就是提供一個共有的靜態工廠(static factory method),他只是一個返回類的實例的靜態方法。
例:
public static Boolean valueOf(boolean b){ return b ? Boolean.TRUE:Boolean.FALSE; }
注意,靜態工廠方法與設計模式中的工廠方法模式不同。類可以通過靜態工廠方法來提供給它的客戶端,而不是通過構造器,提供靜態工廠方法而不是公有的構造器,這樣做具有幾大優勢:
靜態工廠方法,它們有名稱
例如構造器BIgInteger(int,int,Random)返回的BigInteter可能為素數,如果用名為BigInteger.probablePrime的靜態工廠方法來表示,顯然更為清楚。
不必在每次調用它們的時候都創建一個新的對象
這使得不可變類可以使用預先構建好的實例,或者將構建好的實例緩存起來,進行重復利用,從而避免常見不必要的重復對象,因為程序經常請求創建相同的對象,那么創建對象的代價會很高。Boolean.valueOf(boolean)方法說明了這項技術。靜態工廠方法也經常用于實現單例模式。
它們可以返回原返回類型的任何子類型的對象
靈活的靜態工廠方法構成了服務提供者框架(Service Provider FrameWork)的基礎,例如JDBC。
對于JDBC,Connection就是它的服務接口,DriverManager.registerDriver是提供者注冊API,DriverManager.getConnection是服務訪問API,Driver就是服務提供者接口。
例,下列簡單的實現包含了一個服務提供者接口和一個默認提供者:
/** * Created by Newtrek on 2017/10/31. * 服務提供者接口 */ public interface Provider { // 提供服務實例,用服務接口返回 Service newService(); }
/** * Created by Newtrek on 2017/10/31. * 服務接口 */ public interface Service { // TODO: 2017/10/31 服務特有的方法 寫在這兒 }
/** * Created by Newtrek on 2017/10/31. */ public class Services { // 構造保護 private Services(){} // provider映射表,保存注冊的Provider private static final Mapproviders=new ConcurrentHashMap<>(); // 默認provider的名字 public static final String DEFAULT_PROVIDER_NAME=" "; // 注冊默認的Provider public static void registerDefaultProvider(Provider p){ registerProvider(DEFAULT_PROVIDER_NAME,p); } // 注冊provider public static void registerProvider(String name,Provider p){ providers.put(name,p); } /** * 靜態工廠方法返回Service實例 */ public static Service newInstance(){ return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name){ Provider p = providers.get(name); if (p==null){ throw new IllegalArgumentException("No provider registered with name:"+name); } return p.newService(); } }
在創建參數化類型實例的時候,它們是代碼變得更加簡潔
例:假設HashMap提供了這個靜態工廠
public staticHashMap newInstance(){ return new HashMap (); }
那么就可以用下面簡潔的代碼獲取實例了。
Map> m=HashMap.newInstance();
把這些方法放在自己的工具類中是很實用的。不過現在java7,java8已經實現了HashMap構造的類型參數推測
缺點類如果不含公有的或者受保護的構造器,就不能被子類化
它們與其他的靜態方法實際上沒有任何區別
在API文檔中,它們沒有像構造器那樣在API文檔中明確標識出來,因為,對于提供了靜態工廠方法二不是構造器的類來說,要想查明如何實例化一個類,這是非常困難的。可以通過在類或者接口注釋中關注靜態工廠,并遵守標準的命名習慣,可以彌補這一劣勢。下面是靜態工廠方法的一些慣用名稱。
valueOf:實際上是類型轉換方法。
of:valueOf的一種更為簡潔的代替
getInstance:返回的實例是通過方法的參數來描述的,但是不能夠說與參數具有同樣的值。對于Singleton來說,該方法沒有參數,并返回唯一的實例。
newInstance:像getInstance一樣,但newInstance能夠確保返回的每個實例都與所有其它實例不同。
getType:像getInstance一樣,但是在工廠方法處于不同的類中的時候使用。Type表示工廠方法所返回的對象類型。
newType:像newInstance一樣,但是在工廠方法處于不同的類中的時候使用,Type表示工廠方法所返回的對象類型。
簡而言之,靜態工廠方法和公有構造器都各有用處,我們需要理解他們各自的長處。靜態工廠通常更加合適,因此切忌第一反應就是提供公有的構造器,而不先考慮靜態工廠。2.遇到多個構造器參數時要考慮用構建器
這個就是Builder設計模式3.用私有構造器或者枚舉類型強化Singleton屬性
這個是單例模式的注意事項,選擇最好的單例模式實現4.通過私有構造器強化不可實例化的能力
有時候需要編寫一些只包含靜態方法和靜態域的類,這些類的名聲很不好,因為有些人在面向對象的語言中濫用這樣的類來編寫過程化的程序,盡管如此,他們也確實有它們的好處,比如常見的工具類java.lang.Math等,都是這樣。方正這樣不可以實例化的類,最好把他的構造器設置為私有。
例如:
public class UtilityClass{ private UtilityClass(){ throw new AssertionError(); } }5.避免創建不必要的對象
一般來說,最好能重用對象而不是再每次需要的時候就創建一個相同功能的新對象,如果對象是不可變的,他就始終可以被重用。
簡單的例子:字符串
String s = new String("stringtest");//不要這樣做,因為該語句每次執行的時候都會創建一個新的String實例,沒必要 // 改進后的版本,這個版本只用了一個String實例,而不是每次執行的時候都創建一個新的實例。對于再同一臺虛擬機中運行的代碼,只要它們包含相同的字符串自字面常量,該對象就可以被重用。 String s = "stringtest";
對于同時提供了靜態方法和構造器方法的不可變類,通常可以使用靜態工廠方法而不是構造器,以避免創建不必要的對象。
優先使用基本類型,而不是裝箱基本類型,當心無意識的自動裝箱。
也不要錯誤地認為本條目暗示著“創建對象的代價非常昂貴,我們應該盡可能地避免創建對象”,相反,由于小對象地構造器制作很少量地顯示工作,所以,小對象地創建和回收動作是非常廉價地。
通過維護自己的對象池來避免創建對象比不是一種好的做法,除非池中的對象是非常重量級的,一般數據庫連接池常用。
6 消除過期的對象引用不要以為Java有垃圾回收機制,能自動管理內存,自動回收垃圾,就可以不管了,其實不然。
內存泄漏的例子
public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_CAPACITY = 16; public Stack(){ elements = new Object[DEFAULT_CAPACITY]; } public void push(Object object){ ensureCapacity(); elements[size++] = object; } public Object pup(){ if (size == 0){ throw new EmptyStackException(); } return elements[size--]; } private void ensureCapacity(){ if (elements.length == size){ elements = Arrays.copyOf(elements,size*2+1); } } }
這段程序并沒有明顯的錯誤,如果是棧先是增長,然后再收縮,那么,從棧中彈出來的對象將不會被當作垃圾回收,因為里面的數組里引用著它,棧內部維護著這些對象的過期引用,過期引用就是指永遠也不會再被解除的引用。
這類問題的修復方法很簡單:一旦對象引用已經過期,只需清空這些應用即可。
沒必要對于每一個對象引用,一旦程序不再用到它,就把它清空。清空對象引用應該是一種例外,而不是一種規范行為,消除過期引用最好的辦法是讓包含該對象的變量結束其生命周期。一般而言,只要類是自己管理內存,程序員就應該警惕內存泄漏問題,一旦元素被釋放掉,則該元素中包含的任何對象引用都應該被清空。
內存泄漏的另一個常見來源是緩存,一旦你把對象放在緩存中,他就很容易被遺忘掉,從而使得它不再有用之后很長一段時間內仍然留在緩存中。可以用WeakHashMap
內存泄漏的第三個常見來源是監聽器和其他回掉,一般都要取消注冊,或者用弱引用
內存泄漏通常不會表現成明顯的失敗,所以他們可以再一個系統中存在很多年,往往通過仔細檢查代碼,借助于Heap刨析工具才能發現內存泄漏問題。
7 避免使用終結方法終結方法(finalizer)通常是不可預測的,也是很危險的,一般情況下是不必要的。
C++的析構器是回收一個對象所占用資源的常規方法,是構造器所必須的對應物,也可以用來回收其他的非內存資源,而在Java中,一般用try-finally塊來完成類似的工作
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69080.html
摘要:構造器的參數沒有確切地描述其返回的對象,適當名稱的靜態工廠方法更容易使用,也易于閱讀。在文檔中,沒有像構造器那樣明確標識出來,因此,對于提供了靜態工廠方法而不是構造器的類來說,要查明如何實例化一個類,有點困難。 第二章 創建和銷毀對象 第1條 考慮用靜態工廠方法代替構造器 兩者創建對象的形式,例如:構造器是new Boolean();靜態工廠方法是 public static Bool...
摘要:本章中的大部分內容適用于構造函數和方法。第項其他方法優先于序列化第項謹慎地實現接口第項考慮使用自定義的序列化形式第項保護性地編寫方法第項對于實例控制,枚舉類型優先于第項考慮用序列化代理代替序列化實例附錄與第版中項目的對應關系參考文獻 effective-java-third-edition 介紹 Effective Java 第三版全文翻譯,純屬個人業余翻譯,不合理的地方,望指正,感激...
摘要:推薦序前言致謝第一章引言第二章創建和銷毀對象第項用靜態工廠方法代替構造器第項遇到多個構造器參數時要考慮使用構建器第項用私有構造器或者枚舉類型強化屬性第項通過私有構造器強化不可實例化的能力第項優先考慮依賴注入來引用資源第項避免創建不必要的對象 推薦序 前言 致謝 第一章 引言 第二章 創建和銷毀對象 第1項:用靜態工廠方法代替構造器 第2項:遇到多個構造器參數時要考慮使用構建器 第...
摘要:一考慮用靜態工廠方法代替構造器構造器是創建一個對象實例的最基本最常用的方法。開發者在使用某個類的時候,通常會使用一個構造器來實現,其實也有其他方式可以實現的,如利用發射機制。 一、考慮用靜態工廠方法代替構造器 構造器是創建一個對象實例的最基本最常用的方法。開發者在使用某個類的時候,通常會使用new一個構造器來實現,其實也有其他方式可以實現的,如利用發射機制。這里主要說的是通過靜態類工...
摘要:模式下使用來設置各個參數,無法僅通過檢驗構造器參數的有效性來保證一致性,會試圖使用不一致狀態的對象。消除過期的對象引用緩存時優先使用,這些數據結構,及時清掉沒用的項。顯示取消監聽器和回調,或進行弱引用。 創建和銷毀對象 1、靜態工廠方法代替構造器 靜態工廠方法有名稱,能確切地描述正被返回的對象。 不必每次調用都創建一個新的對象。 可以返回原返回類型的任何子類對象。 創建參數化類型實例...
閱讀 3391·2023-04-25 14:07
閱讀 3458·2021-09-28 09:35
閱讀 2091·2019-08-30 15:55
閱讀 1405·2019-08-30 13:48
閱讀 2502·2019-08-30 13:16
閱讀 3202·2019-08-30 12:54
閱讀 3238·2019-08-30 11:19
閱讀 1876·2019-08-29 17:17