天天看點

設計模式之簡單的單例模式如何實作

單例模式的特點:在同一時期,某個類的對象一定最多隻有1個!也許會嘗試多次的擷取對象,但是,擷取到的一定是同一個對象!

假設項目中有King類:

public class King {
}      

很顯然,目前它并不是單例的,因為,可以:

King k1 = new King();
King k2 = new King();
King k3 = new King();      

以上代碼就建立了3個King類型的對象!如果要實作單例,首先,就必須限制構造方法的通路,例如:

public class King {
    private King() {
    }
}      

每個類中都可以有若幹個構造方法,如果某個類沒有顯式的聲明任何構造方法,編譯器就會自動添加1個公有的、無參數的構造方法!如果類中已經聲明任何構造方法,則編譯器不會自動添加構造方法!

由于将構造方法聲明為私有的,則原有的King k1 = new King();這類代碼就不能用于建立對象了!

限制構造方法的通路,其目的是“不允許随意建立對象”,并不是“不允許建立對象”,在King類的内部,還是可以建立對象的,可以添加方法,傳回内部建立的對象:

public class King {
    private King king = new King();
   
    private King() {
    }
   
    public King getInstance() {
        return king;
    }
}      

是以,當需要King類型的對象時,可以通過getInstance()方法來擷取!

但是,以上代碼是不可行的!因為,如果要調用getInstance()方法,必須先擷取King的對象,而擷取King對象的唯一方式就是調用getInstance()方法!為了解決這個問題,必須在getInstance()方法的聲明之前添加static修飾符,最終,就可以通過類名.方法名()的文法格式來調用方法了!同時,由于“被static修飾的成員,不可以通路其它未被static修飾的成員”,是以,全局屬性king也必須被static修飾:

public class King {
    private static King king = new King();
   
    private King() {
    }
   
    public static King getInstance() {
        return king;
    }
}      

至此,基本的單例模式的代碼就設計完成了!

以上代碼是“餓漢式”的單例模式,另外,還有“懶漢式”的單例模式!

基本的懶漢式單例模式的代碼是:

public class King {
    private static King king = null;
   
    private King() {
    }
   
    public static King getInstance() {
        if (king == null) {
            king = new King();
        }
        return king;
    }
}      

注意:以上代碼是多線程不安全的!

在開發領域中,隻要資料的産生、變化不是開發人員預期的,就稱之為“不安全”,也就是“資料安全問題”。

為了保障線程安全,應該為以上建立對象的代碼片斷“加鎖”,例如:

public class King {
    private static King king = null;
   
    private King() {
    }
   
    public static King getInstance() {
        synchronized ("hello") {
            if (king == null) {
                king = new King();
            }
        }
        return king;
    }
}      

當然,無論是哪個線程在什麼時候執行以上代碼,都必須先“鎖住”代碼片斷後才能開始執行,是沒有必要的,“鎖”的性能消耗是浪費的,是以,可以進一步調整為:

public class King {
    private static King king = null;
   
    private King() {
    }
   
    public static King getInstance() {
        if (king == null) { // 判斷有沒有必要鎖定接下來的代碼
            synchronized ("java") {
                if (king == null) { // 判斷有沒有必要建立對象
                    king = new King();
                }
            }
        }
        return king;
    }
}      

繼續閱讀