爲何使用單例模式?

咱們在編程中最經常使用的模式就是單例模式了,然而單例模式都用在什麼場合?爲何不用靜態方法而要用單例模式呢?要搞清這些問題,須要從靜態方法和非靜態方法的區別和聯繫提及。html

 

1、靜態方法常駐內存,非靜態方法只有使用的時候才分配內存?java

 

通常都認爲是這樣,而且怕靜態方法佔用過多內存而建議使用非靜態方法,其實這個理解是錯誤的。c++

爲何會這樣,先從內存分配開始提及:數據庫

託管堆的定義:對於32位的應用程序來講,應用程序完成進程初始化後,CLR將在進程的可用地址空間分配一塊保留的地址空間,它是進程(每一個進程可以使用4GB)中可用地址空間上的一塊內存區域,但並不對應任何物理內存,這塊地址空間便是託管堆。編程

託管堆有分爲多個區域,其中最重要的是垃圾回收堆(GC Heap)和加載堆(Loader Heap),GC Heap用於存儲對象實例,受GC管理;Loader Heap又分爲High-Frequency Heap、Low-Frequency Heap和Stub Heap,不一樣的堆上又存儲不一樣的信息。Loader Heap最重要的信息就是元數據相關的信息,也就是Type對象,每一個Type在Loader Heap上體現爲一個Method Table(方法表),而Method Table中則記錄了存儲的元數據信息,例如基類型、靜態字段、實現的接口、全部的方法等等。Loader Heap不受GC控制,其生命週期爲從建立到AppDomain卸載。(摘自《你必須知道的.Net》)c#

由此咱們就明白了,靜態方法和非靜態方法,在內存裏其實都放在Method Table裏了,在一個類第一次被加載的時候,它會在Loader Heap裏把靜態方法,非靜態方法都寫入Method Table中,並且Loader Heap不受GC控制,因此一旦加載,GC就不會回收,直到AppDomain卸載安全

由此咱們也明白了,靜態方法和非靜態方法,他們都是在第一次加載後就常駐內存,因此方法自己在內存裏,沒有什麼區別,因此也就不存在」靜態方法常駐內存,非靜態方法只有使用的時候才分配內存「這個結論了。多線程

 

2、靜態方法和非靜態方法的區別?工具

 

在內存中的區別是,非靜態方法在建立實例對象時,由於屬性的值對於每一個對象都各不相同,所以在new一個實例時,會把這個實例屬性在GC Heap裏拷貝一份,同時這個new出來的對象放在堆棧上,堆棧指針指向了剛纔拷貝的那一份實例的內存地址上。而靜態方法則不須要,由於靜態方法裏面的靜態字段,就是保存在Method Table裏了,只有一份。性能

所以靜態方法和非靜態方法,在調用速度上,靜態方法速度必定會快點,由於非靜態方法須要實例化,分配內存,但靜態方法不用,可是這種速度上差別能夠忽略不計。

 

3、爲何要有非靜態方法?

 

早期的結構化編程,幾乎全部的方法都是「靜態方法」,引入實例化方法概念是面向對象概念出現之後的事情了,區分靜態方法和實例化方法不能單單從性能上去理解,建立c++,java,c#這樣面嚮對象語言的大師引入實例化方法必定不是要解決什麼性能、內存的問題,而是爲了讓開發更加模式化、面向對象化。這樣說的話,靜態方法和實例化方式的區分是爲了解決模式的問題。

接下來繼續思考,若是咱們所有用靜態方法,不用非靜態方法,不是同樣能實現功能嗎?是的,沒錯,可是你的代碼是基於對象,而不是面向對象的,由於面向對象的繼承和多態,都是非靜態方法。

第二個緣由是爲何不建議都用靜態方法,咱們若是多線程的狀況下,若是靜態方法使用了一個靜態字段,這個靜態字段能夠會被多個線程修改,所以說若是在靜態方法裏使用了靜態變量,這就會有線程安全問題,固然了,就算不是多線程,由於靜態字段只有一份,一樣會有被其餘地方修改的問題。

 

從這三點咱們得出的結論以下:

 

1、 何時用靜態方法,何時使用非靜態方法?

 

既然靜態方法和實例化方式的區分是爲了解決模式的問題,若是咱們考慮不須要繼承和多態的時候,就可使用靜態方法,但就算不考慮繼承和多態,就一律使用靜態方法也不是好的編程思想。

從另外一個角度考慮,若是一個方法和他所在類的實例對象無關,那麼它就應該是靜態的,不然就應該是非靜態。所以像工具類,通常都是靜態的。

 

2、 爲何使用單例模式而不用靜態方法?

 從面相對象的角度講:

雖然都能實現目的,可是他們一個是基於對象,一個是面向對象的,就像咱們不面相對象也能解決問題同樣,面相對象的代碼提供一個更好的編程思想。

若是一個方法和他所在類的實例對象無關,那麼它就應該是靜態的,反之他就應該是非靜態的。若是咱們確實應該使用非靜態的方法,可是在建立類時又確實只須要維護一份實例時,就須要用單例模式了。

好比說咱們在系統運行時候,就須要加載一些配置和屬性,這些配置和屬性是必定存在了,又是公共的,同時須要在整個生命週期中都存在,因此只須要一份就行,這個時候若是須要我再須要的時候new一個,再給他分配值,顯然是浪費內存而且再賦值沒什麼意義,因此這個時候咱們就須要單例模式或靜態方法去維持一份且僅這一份拷貝,但此時這些配置和屬性又是經過面向對象的編碼方式獲得的,咱們就應該使用單例模式,或者不是面向對象的,但他自己的屬性應該是面對對象的,咱們使用靜態方法雖然能一樣解決問題,可是最好的解決方案也應該是使用單例模式。

從功能上講:單例模式能夠控制單例數量;能夠進行有意義的派生;對實例的建立有更自由的控制;

3、其餘:

 

數據庫鏈接能不能作SingleTon?

若是是簡單地把一個connection對象封存在單例對象中,這樣是錯誤的,所以鏈接池裏有多個連接能夠用,若是使用SingleTon,那在WEB訪問時,就只能用一個數據庫連接,那不是死的很慘?

可是連接池可使用單例模式,初始化的時候建立譬如100個connection對象,而後再須要的時候提供一個,用過以後返回到pool中,咱們用單例模式,是保證鏈接池有且只有一個。

再舉個例子,好比DAL層寫好一個調用數據庫表的類,在BLL層應用此類時,若是每次都new建立的話須要頻繁的建立和回收,而DAL層這個類裏又沒有和對象相關的值變量,因此不須要每次都new一個,這時候就能夠用單例模式來建立這個DAL實例。

 

轉載:

http://www.cnblogs.com/seesea125/archive/2012/04/05/2433463.html

http://blog.csdn.net/jiary5201314/article/details/52628882

相關文章
相關標籤/搜索