【java設計模式初探】之單例模式
在java的幾十種設計模式中,可能單例模式算是最容易理解的吧!因為不論是目前的我自己,還是偶爾麵試的別人,能稍微講清楚的,基本就是單例模式。
什麼叫單例模式?顧名思義,就是單一的實例,唯一的實例。也就是說對於某個java類來說,他的實例對象最多隻能創建一個。
那麼,稍微有點java基礎的同學都知道,我們自己創建對象最基本的方式就是使用new關鍵字,通過類定義的構造器來創建。就比如有這樣一個類:
public class Earth{
public Earth(){
}
}
我們自己創建該類的對象,一般就是new Earth(),那麼很顯然的,這種情況下我們每new一次,這個類就會有一個新的對象實例。
如此一來,也就是說隻要這個類有了可以供外部調用的構造器,那麼就必然無法控製這個類的實例個數。
也就是說,如果我們希望這個類的實例隻有一個,就必須把類的構造器訪問權限設置成private,使得外部無法調用。就如下邊這樣:
public class Earth{
private Earth(){
}
}
那麼問題又來了,既然類的構造器都是private,那麼我們又該如何創建這個類的實例對象呢?
這時候就需要知道一個基礎的知識點:**實際上在java中創建對象,歸根結底都隻能使用構造器創建,不論是使用框架(框架內部)還是直接使用普通代碼!**
也就是說,即使這個類的構造器是private的,我們也還是隻能使用類構造器來創建這個類的實例對象。
如此一來,便又涉及到一個java很基礎的知識點,我們知道private修飾的方法隻有當前類才能使用,那麼很顯然的,對於構造器是private的類,我們也就隻能在這個類的內部創建類的實例對象了。
public class Earth{
private Earth(){
}
Earth earth=new Earth();
}
那麼問題又來了,看起來我們在當前類中創建了一個該類的實例對象,但是我們知道java中非static修飾的變量是屬於對象的,而這裏我們並不能在外部創建類的實例對象,那麼就還是無法獲取到我們自己創建的實例。
怎麼辦呢?此時外部能訪問的隻有這個類本身,那麼如果要外部能獲取到我們在類中創建的實例,就隻能使這個實例是屬於類的,也就是說需要讓它變成static。
public class Earth{
private Earth(){
}
static Earth earth=new Earth();
}
好了,這樣一來,實際上一個基本的單例就算是實現了,我們已經保證了這個類隻會有一個實例對象,直接使用Earth.earth來獲取就好了。
但是呢,這並不是我們要說的單例模式的規範寫法。**因為在java規範中,類的屬性一般是需要對外隱藏的,也就是說這個自己創建的實例對象也需要聲明為private**。
public class Earth{
private Earth(){
}
private static Earth earth=new Earth();
}
這樣的話,我們外部似乎又沒法獲取到這個類的對象了,怎麼辦呢?那就隻能提供一個對外公開的方法,讓外部能根據這個方法來獲取到類的對象實例。當然了,這個方法自然也是要屬於類的,這樣才能在沒有對象多的情況下調用:
public class Earth{
private Earth(){
}
private static Earth earth=new Earth();
public static Earth getInstance(){
return earth;
}
好了,到了這裏,我們一個比較規範的單例模式的類便算是真正的完成了,既可以保證這個類的實例是單一的,又遵循了基本的java規範。
總結一下就會發現其實就是三個簡單而必須的步驟:
一、構造器私有化
二、自己創建自己的私有並且靜態的實例對象
三、提供一個外部能訪問的靜態方法,返回自己的實例對象
上邊這種模式,被稱為單例模式中的餓漢式。
何為餓漢?餓,可以理解為迫不及待,也就是等不及要吃東西。放在我們代碼中,就是說在一開始就創建了實例對象,編譯完就立馬初始化了這個實例對象。
那麼除開餓漢式,還有一個很常用的就是懶漢式了(實際還有一個雙重鎖模式,這裏暫時不談),基本代碼如下:
public class Earth{
private Earth(){
}
private static Earth earth=null;
public static Earth getInstance(){
if(earth==null){
earth=new Earth();
}
return earth;
}
和上邊餓漢式的區別在於,並沒有在定義Earth變量的時候就給他初始化,而是給了一個初始值null。隻不過這裏雖然顯示的寫了null,實際上是可以不寫的。
因為即使我們不顯示的給,在這個類編譯完之後也會給所有的變量初始化賦值,基本類型有各自獨特的初始值,比如int類型的是0,而引用類型的默認初始值都是null。
與此同時,我們把具體對象的實例化放在了提供給外部調用的方法中,並且做了一定的判斷,隻有當這個對象是null的時候才會創建一個對象。
這裏的判斷是很必要的,如果不做判斷,那麼每調用一次就是一個新的對象,無法保證單一實例,跟直接構造器創建就沒了區別。
那麼有了代碼,懶漢式也就比較好理解了。**何謂懶漢?懶,就是做什麼事都要一拖再拖,直到非做不可的時候才做。放在我們代碼中,也就是在必須要獲取實例對象的時候才會創建一個實例,在第一次調用getInstance方法的時候才會創建實例。**
單例模式很簡單,在實際項目中也應用非常的廣泛。例如某些數據量很小的,直接定義死的,但是有時候可能因為業務需求的變化需要手動改變的一些公用數據,我們就可以不放在數據庫,而是直接放在配置文件中,然後在項目中加載一次,放到內存中。
最後更新:2017-09-17 12:03:09