單例模式解決的問題就是可以確定一個類在記憶體中的對象唯一性。
它可以實作資料共享,比如A程式需要用到一個配置檔案,B程式也要用到一個配置檔案,現在有個需求:A程式如果修改了這個配置檔案,B程式需要知道更新過後的這個配置檔案。這時候就要使用單例模式來確定配置檔案的唯一性。
那麼如何確定對象的唯一性?
1 不允許其他程式用new來建立該類的對象。
2 在該類中建立一個本來執行個體。
3 對外提供一個方法讓其他程式可以擷取對象。
那麼如何實作?
1 私有化該類的構造函數。這樣類外部無法通路構造函數進行建立對象。
2 通過new在本類中建立一個本類對象。
3 定義一個公有的方法,将建立的對象傳回,提供給外部使用。
代碼實作:
class Single
{
static Single sg = new Single();
private Single()
{
}
//外部不能建立Single對象,還要擷取這個對象,那麼擷取的函數必須是static,
//這樣可以通過類名調用了。
public static Single getInstance()
{
return sg;
}
//--------------------------------------------------
private int num; //單例裡面的資料
public void SetNum(int num)
{
this.num = num;
}
public int GetNum()
{
return this.num;
}
//--------------------------------------------------
}
class SingleDemo
{
public static void main(String[] args)
{
Single s = Single.getInstance(); //①
Single s = Single.sg; //②
}
}
代碼分析:
1 既然②也可以通路單例對象,那麼為什麼還要用①這種形式來通路呢?
因為通過②這種方式通路單例,不如形式①通過函數通路可控,①的可控展現在可以在函數内部對sg進行相關處理,(比如取不到單例就傳回null)
說白了①方式是函數調用,函數内部可以寫更多的代碼來控制着單例。是以一般推薦①方式來通路。
2 為了避免不安全的調用,我們習慣将單例改成private權限,即
static Single sg = new Single() ==> private static Single sg = new Single()
接下來我們進一步修改:
class Single
{
private static Single sg = new Single(); //加了一個private限制 ③
private Single()
{
}
public static Single getInstance()
{
return sg;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
System.out.println(s1 == s2); //結果為true
}
}
我們看到上面代碼的③:
如果我們延遲加載,在調用getInstance方法的時候再建立對象,我們需要修改一下代碼如下:
//但是這種方式存在隐患,在多線程裡面使用有時候會無法保證單例的唯一性。
class Single
{
//類加載進來,沒有對象,隻有調用了getInstace方法時,才會建立對象。
private static Single sg = null;
private Single()
{
}
public static Single getInstance()
{
if(sg == null)
a = new Single();
return sg;
}
}
注意:但是這種方式存在安全隐患,在多線程裡面使用時候會無法保證單例的唯一性。