設計模式:單例模式(關於餓漢式和懶漢式)
定義
- 單例模式是一種常見的設計模式,目的是保證一個類中只能有一個實例,而且自行實例化並向整個系統提供這個實例,避免頻繁創建對象,節約內存
優缺點
優點
- 單例類只有一個實例,節省了內存資源,對於一些需要頻繁創建銷燬的對象,使用單例模式可以提高系統性能;
- 單例模式可以在系統設置全局的訪問點,優化和共享數據,例如前面說的Web應用的頁面計數器就可以用單例模式實現計數值的保存。
缺點
- 單例模式一般沒有接口,擴展的話除了修改代碼基本上沒有其他途徑。
類加載順序
- 先執行父類的靜態代碼塊和靜態變量初始化,靜態代碼塊和靜態變量的執行順序跟代碼中出現的順序有關。
- 執行子類的靜態代碼塊和靜態變量初始化。
- 執行父類的實例變量初始化
- 執行父類的構造函數
- 執行子類的實例變量初始化
- 執行子類的構造函數
同時類加載的過程是線程私有的,別的線程無法進入
如果類已經被加載:靜態代碼塊和靜態變量不在執行,再創建類對象時,只執行與實例相關的變量初始化和構造方法
Static關鍵字
一個類中如果有成員變量或者方法被static關鍵字修飾,那麼該成員變量或方法將獨立於該類的任何對象。它不依賴類特定的實例,被類的所有實例共享,只要這個類被加載,該成員變量或方法就可以通過類名去進行訪問,它的作用用一句話來描述就是,不用創建對象就可以調用方法或者變量。
下面將列舉幾種單例模式的實現方式,其關鍵方法都是用static修飾的,並且,爲了避免單例的類被頻繁創建對象,我們可以用private的構造函數來確保單例類無法被外部實例化。
懶漢和餓漢模式
單例模式一般分爲兩種一種是餓漢式一種是懶漢式
餓漢式:在類加載時就完成了初始化,所以類加載比較慢,單獲取對象的速度比較快
懶漢式:在類加載時不初始化,等到第一次被使用時才初始化
Java代碼實現
餓漢式(可用)
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
- 在類加載的時候就完成了實例化,避免了多線程的同步問題,但是因爲在類加載就實例化了,沒有達到懶加載的效果,如果該實例沒有被使用,內存就浪費了
懶漢式
普通的懶漢式
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
只有在方法第一次被訪問時纔會實例化,達到了懶加載的效果,對**getInstance()加了鎖的處理,保證了同一時刻只能有一個線程訪問並獲得實例,**但是因爲synchronized是修飾整個方法,每個線程訪問都要進行同步,但是這個方法只執行一次實例化代碼就夠了,每次同步導致效率低下
雙重檢查懶漢式(推薦,可用)
public class Singleton {
// volatile 讓變量每次在使用的時候,都從主存中取。而不是從各個線程的“工作內存”
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
這種寫法使用了兩個if判斷,並且同步的不是方法,而是代碼塊,效率較高。防止多次初始化對象
靜態內部類
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
- 這種靜態內部類方式在Singleton類被裝載時不會立即實例化,而是在需要實例化時,調用getInstance方法,纔會裝載Single通Instance類,從而完成對象的實例化
- 因爲類的靜態屬性只會在第一次加載類的時候初始化,也就保證了SingletonInstance中的對象只會被實例化一次,並且這個過程也是線程安全的
枚舉
public enum Singleton {
INSTANCE;
}
- 線程安全:因爲Java虛擬機在加載枚舉類的時候會使用ClassLoader的方法,這個方法使用了同步代碼塊來保證線程安全。
- 避免反序列化破壞對象,因爲枚舉的反序列化並不通過反射實現。