Java - static 關鍵字 vs. 單例 (Singleton) 設計模式

By sunwc 2023-03-21 Java

static 關鍵字: 有些時候,我們希望「某些特定資料在記憶體中只有一份」,且這份資料是大家共享的,而不會隨著物件而不同的,例如:存款利率、最低存款金額、工具類

access      靜態變數    實例變數
類別        Yes           No

物件        Yes           Yes

static 特性:

  • static 可以修飾 屬性、方法、程式區塊、內部類
    • 靜態程式區塊 (static block):(用於類別初始化) 隨著類別加載時,就會執行,且只會執行一次
    static {
      System.out.println("只要本類別在加載時,我會被輸出");
    }
    
    • 非靜態程式區塊 (non-static block) 隨著每次物件被new時,就會被執行;執行順序優於建構子 (constructor)
    {
      System.out.println("只要本類別在new一個物件時,我會被輸出");
    }
    
  • 靜態變數/靜態方法 是與類別 同時加載的,因此 靜態變數 是早於物件創建
  • 由於類別只會加載一次,因此靜態修飾的結構只會在記憶體中存在一份資料,存在在方法區的靜態域
  • 靜態方法只可以存取靜態變數與靜態方法(早出生不能調晚出生的);實例方法可以存取靜態結構與實例結構(晚出生能調早出生的),原因是看在生命週期被創建的時間點

單例設計模式:

  1. 所謂類別的單例設計模式,就是採取一定的方法保證在整個程式系統中,對某個類別只能存在一個物件實例。

  2. 如何實現: 具體參考如下

  3. 單例模式的優點: 由於單例模式指生成一個實例,減少了系統性能開銷,當一個物件的產生需要比較多的資源時,如讀取配置、產生其他依賴物件時,則可以通過在應用程式啟動時直接產生一個單例物件,然後永久駐留記憶體的方式來解決,例如 java.lang.Runtime

  1. 現實例子:
    • 網站的計數器
    • 應用程式的日誌應用
    • 資料庫連線池
    • 讀取配置文件的類別
    • Application 就是單例的典型應用
    • Windows的Task Manager(任務管理器)
    • Windows的Recycle Bin(資源回收桶)

單例設計模式 方式一

// 餓漢式
public class SingletonTest {
	
	public static void main(String[] args) {
		Bank bank1 = Bank.getInstance();
		Bank bank2 = Bank.getInstance();
		
		// 同一個地址值
		System.out.println("bank1 == bank2 : " + (bank1 == bank2));
	}

}

class Bank {
	
	// 1. 私有化類別的建構子
	private Bank() {
		
	}
	
	// 2. 類別的內部創建類別的私有物件,且宣告為靜態的
	private static Bank instance = new Bank();
	
	// 3. 提供公共靜態的方法,回傳類別的物件
	public static Bank getInstance() {
		return instance;
	}
}

輸出結果:
```txt
bank1 == bank2 : true

單例設計模式 方式二

// 懶漢式
public class SingletonTest2 {

	public static void main(String[] args) {
		
		Order order1 = Order.getInstance();
		Order order2 = Order.getInstance();
		
		// 同一個地址值
		System.out.println("order1 == order2 : " + (order1 == order2));
	}

}

class Order {

  static {
		System.out.println("static block 類別加載中…");
	}
	
	
	// 1. 私有化類別的建構子
	private Order() {

	}

	// 2. 類別的內部宣告類別變數,但不初始化
	private static Order instance = null;

	// 3. 提供公共靜態的方法,回傳類別的物件
    // 要調用的時候再new(非執行緒安全) => 方法加上 synchronized 關鍵字就成了同步方法(執行緒安全)
	public static synchronized  Order getInstance() {
		if (instance == null) {
			// 只new一次
			instance = new Order();
		}

		return instance;
	}

	{
		System.out.println("non-static block 類別物件被new出來了…");
	}
}

輸出結果:

static block 類別加載中…
non-static block 類別物件被new出來了…
order1 == order2 : true