1. 程式人生 > >【java設計模式初探0】_單例模式

【java設計模式初探0】_單例模式

在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方法的時候才會建立例項。

單例模式很簡單,在實際專案中也應用非常的廣泛。例如某些資料量很小的,直接定義死的,但是有時候可能因為業務需求的變化需要手動改變的一些公用資料,我們就可以不放在資料庫,而是直接放在配置檔案中,然後在專案中載入一次,放到記憶體中。

相關推薦

no