Java設計模式之單例模式理解
前言
本片博客主要記錄Java23種設計模式中的創建型模式中的單例模式。單例模式可分為兩類,一種是餓漢式,一種是懶漢式。餓漢式的三種設計方式(靜態變量方式、靜態代碼塊方式、枚舉方式),懶漢式(單鎖檢查方式、雙鎖檢查方式、靜態內部類方式),以及破壞單例模式的兩種方式:序列化反序列化,反射。
設計模式,是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性
單例模式結構
私有的構造方法【核心】
私有的、靜態的實例化變量應用
提供一個公有的、靜態的獲取類實例對象方法
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。它提供了一種創建對象的最佳方式。
單例模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
餓漢式
靜態變量方式
- 直接在創建對象時賦值
package hello;
public class Hello {
public static void main(String[] args) {
//只能通過getSingleton方法獲取,不能通過new方法創建
Singleton singleton = Singleton.getSingleton();
Singleton singleton11 = Singleton.getSingleton();
//通過hashCode查看是否是同一個對象
System.out.println(singleton.hashCode());
System.out.println(singleton11.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能創建了
private Singleton(){
}
//自己創建一個對象
private static Singleton singleton = new Singleton();
//給外界提供一個方法用於訪問
public static Singleton getSingleton(){
return singleton;
}
}
靜態代碼塊方式
- 在靜態代碼塊里賦值
package hello;
public class Hello {
public static void main(String[] args) {
//只能通過getSingleton方法獲取,不能通過new方法創建
Singleton singleton = Singleton.getSingleton();
Singleton singleton11 = Singleton.getSingleton();
//通過hashCode查看是否是同一個對象
System.out.println(singleton.hashCode());
System.out.println(singleton11.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能創建了
private Singleton(){
}
//自己創建一個對象,但是不實例
private static Singleton singleton;
//通過靜態代碼塊賦值
static {
singleton = new Singleton();
}
//給外界提供一個方法用於訪問
public static Singleton getSingleton(){
return singleton;
}
}
枚舉方式
由於上面檢測代碼相同,就不在這裡重複複製了。
只需要把class Singleton改為下面就行了
enum Singleton{
SINGLETON;
}
懶漢式
單鎖檢查模式
package hello;
public class Hello {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton11 = Singleton.getSingleton();
//通過hashCode查看是否是同一個對象
System.out.println(singleton.hashCode());
System.out.println(singleton11.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能創建了
private Singleton(){ }
//自己聲明一個對象
private static Singleton singleton;
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
//判讀singleton是否為null,如果是null就創建,否者直接返回
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
雙重檢查鎖模式
上面的驗證都是一樣的,這裡只顯示Singleton類就行
class Singleton{
//私有構造方法,這樣外界就不能創建了
private Singleton(){ }
//自己聲明一個對象
private static volatile Singleton singleton;
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
//判讀singleton是否為null,如果是null就創建,否者直接返回
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
靜態內部類實現
-
靜態內部類單例模式是一種優秀的單例模式,是比較常用的單例模式。在沒有加任何鎖時保證線程安全,並且沒有任何性能影響和空間浪費。
-
在加載Singleton時不會初始化singleton,只有第一次調用getSingleton()時。JVM加載SingletonHolder初始化singleton。
class Singleton{
//私有構造方法,這樣外界就不能創建了
private Singleton(){ }
//定義一個靜態內部類
private static class SingletonHolder{
//只會初始化一次
private static final Singleton singleton= new Singleton();
}
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
return SingletonHolder.singleton;
}
}
破壞單例模式
- 破壞單例模式的方式有兩種一種是序列化反序列化,另一種是反射,這裡我們指記錄反射
- 道高一尺,魔高一丈。有模式就會有破壞,有破壞還會有防破壞,但是還有反反破壞。這裏面就多了。
通過反射破壞單例模式
package hello;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Hello {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//獲取Singleton位元組碼對象
Class<Singleton> singletonClass = Singleton.class;
//獲取無參構造方法
Constructor<Singleton> declaredConstructor = singletonClass.getDeclaredConstructor();
//取消訪問檢查
declaredConstructor.setAccessible(true);
//創建Singleton對象
Singleton singleton = (Singleton) declaredConstructor.newInstance();
Singleton singleton1 = (Singleton) declaredConstructor.newInstance();
//通過hashCode查看是否是同一個對象
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能創建了
private Singleton(){ }
//定義一個靜態內部類
private static class SingletonHolder{
//只會初始化一次
private static final Singleton singleton= new Singleton();
}
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
return SingletonHolder.singleton;
}
}
單例模式優缺點
優點:
- 單例模式在內存中只有一個實例,減少內存開支,特別是一個對象需要頻繁地創建銷毀時,而且創建或銷毀時性能又無法優化,單例模式就非常明顯了
- 單例模式只生成一個實例,減少系統的性能開銷
- 單例模式可以避免對資源的多重佔用
- 單例模式可以在系統設置全局的訪問點,優化和共享資源訪問
缺點: - 不適用於變化的對象
- 由於單例模式沒有抽象層,所以擴展困難
- 單例類的職責過重,在一定程度上違背了「單一職責原則」
- 單一職責原則:一個類,應該只有一個職責