設計模式: 對問題行之有效的解決方式, 其實它是一種思想.
單例設計模式
解決的問題:就是可以保證一個類在內存中的對象唯一性. 即單個實例.
比如對于A 和 B 兩個程序使用同一個配置信息對象時, A 對配置信息作出修改, B 也與之對應的更新配置信息, 即需要保證該對象的唯一性.
如何保證對象唯一性呢?
不允許其他程序用 new 創建該類對象
在該類中創建一個本類實例
對外提供一個方法讓其他程序可以獲取該對象.
步驟:
私有化該類構造函數
通過 new 在本類中創建一個本類對象
定義一個公有方法, 將創建的對象返回
兩種方式
餓漢式: 類一加載, 對象就存在了.
懶漢式: 類加載進來, 沒有對象, 只有調用 getInstance 方法時, 才會創建對象.
也稱為 單例設計模式的延遲加載模式. 但是, 懶漢式在多線程訪問時,存在安全隱患.
// 第一種方式: 餓漢式
class Single
{
Single s = new Single(); // 在本類中創建一個本類對象
private Single(){} // 私有化該類構造函數
// 定義一個公有方法, 將創建的對象返回. 用于返回對象 s, 所以返回類型 Single
public Single getInstance()
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single ss = Single.getInstance();
}
}
/*
分析一: main 函數中, getInstance 方法調用不能使用對象調用, 只能使用類名調用.
所以 Single 類中該方法需要使用 static 修飾.
分析二: getInstance 方法為靜態方法, 它訪問的內容必須是靜態的,所以對象 s 也需要靜態修飾.
*/
// 改進
class Single
{
private static Single s = new Single();
private Single(){}
public static Single getInstance() // 提供該方法訪問實例對象 s, 是為了對象的可控
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single ss = Single.getInstance();
}
}
// 第二種方式: 懶漢式
// 類加載進來, 沒有對象, 只有調用 getInstance 方法時, 才會創建對象.
class Single2
{
private static Single2 s = null;
private Single(){}
public static Single2 getInstance()
{
if(s==null)
s = new Single2();
return s;
}
}
// 示例:下列代碼的輸出結果
class SingleDemo
{
public static void main(String[] args)
{
Test t1 = Test.getInstance();
Test t2 = Test.getInstance();
t1.setNum(10);
t2.setNum(20);
System.out.println(t1.getNum()); // 輸出 20
System.out.println(t2.getNum()); // 輸出 20
}
}
class Test
{
private int num;
private static Test t = new Test();
private Test(){}
public static Test getInstance()
{
return t;
}
public void setNum(int num)
{
this.num = num;
}
public int getNum()
{
return num;
}
}
// 懶漢式二:避免多線程同時調用getInstance()方法, 可以使用關鍵字synchronized
class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){}
public synchronized static LazySingleton getInstance() {
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
// 懶漢式三:為提高系統性能,對"instance = new LazySingleton()"進行鎖定
class LazySingleton{
private static LazySingleton instance = null;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(instance == null){ // 此處,有可能造成單例對象不唯一
synchronized(LazySingleton.class){
instance = new LazySingleton();
}
}
return instance;
}
}
// 懶漢式四:雙重檢查鎖定(Double-Check Locking)
class LazySingleton{
// 注意,此處增加修飾符 volatile
private volatile static LazySingleton instance = null;
private LazySingleton(){}
public static LazySingleton getInstance(){
// 第一重判斷
if(instance == null){
// 鎖定代碼塊
synchronized(LazySingleton.class){
// 第二重判斷
if(instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
//單例第三種方式: Initialization Demand Holder(IoDH)技術
// 在單例類中增加一個靜態(static)內部類
class Singleton{
private Singleton(){}
// 靜態類
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
public static void main(String args[]){
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
_參考資料_
- [JavaSE 基礎視頻(畢向東)](https://www.bilibili.com/video/av3092292/#page=4)
- [單例模式中的雙重檢查](http://blog.csdn.net/chenchaofuck1/article/details/51702129)