單例設計模式
單例設計模式是一種常見的軟體設計模式,它能保證一個類只能有一個例項,並提供一個全域性訪問的方法。單例設計模式適用於那些需要頻繁建立物件然後銷燬物件的場景,因為頻繁的建立物件會消耗大量的系統資源,使用單例設計模式可以避免對資源的浪費。
單例設計模式特點:
- 該類在JVM中只有唯一的類例項
- 它必須對外界提供統一的,獲取該類例項的方法
在Java中,實現單例設計模式分為多種:
- 餓漢式:在類載入階段時就建立例項,該模式下不會出現執行緒安全問題,因此並不需要加鎖,如下面的程式碼
public class Main {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1);
System.out.println(singleton2);
}
}
class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getSingleton() {
return singleton;
}
}
執行結果如下:
leetcode.Singleton@58372a00
leetcode.Singleton@58372a00
- 懶漢式:懶漢式指的是在第一次使用該類例項時才建立該類例項,這種方式的好處是可以延遲載入,缺點是在多執行緒下可能會引起執行緒安全問題,例如建立該類的多個例項,從而違背了單例設計模式的特性。因此在使用懶漢式需要在建立該類例項時需要加上鎖,如下面的程式碼:
public class Main {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1);
System.out.println(singleton2);
}
}
class Singleton {
private static Singleton singleton;
private Singleton() {}
public synchronized static Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
程式碼執行結果如下:
leetcode.Singleton@7ba4f24f
leetcode.Singleton@7ba4f24f
- 懶漢式2(靜態內部類方式)
當然了,除了上面那種常用的懶漢式建立單例的方法,也可以透過內部類方式透過懶漢式建立單例例項,這種本質上還是懶漢式,如下面的程式碼:
public class Main {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1);
System.out.println(singleton2);
}
}
class Singleton {
private static class Builder {
private static final Singleton singleton = new Singleton();
}
private Singleton() {}
public synchronized static Singleton getSingleton() {
return Builder.singleton;
}
}
執行結果如下:
leetcode.Singleton@3b9a45b3
leetcode.Singleton@3b9a45b3
- 透過列舉實現(推薦用法)
列舉本質上就是單例模式的一種體現,其思路也是懶漢式,列舉之所以能夠作為單例,原因如下:
4.1 執行緒安全性:列舉型別的例項化是在JVM層面完成的,因此列舉類例項化是執行緒安全的。當JVM載入列舉類時,JVM會確保所有宣告的列舉類僅初始化一次,並且在這個過程中不會發生併發問題。
4.2 防止反射攻擊:Java列舉類的構造器是私有的,並且Java不允許透過反射機制建立新的列舉示例,這也就意味著一旦定義了一個列舉例項,就無法透過反射以及其它手段建立該列舉類的額外例項。
4.3 程式碼簡潔:列舉的語法非常的簡潔,如下面的程式碼:
public class Main {
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton2 == singleton1);
}
}
enum Singleton {
INSTANCE;
}