天天看點

Java面試 - 什麼是單例設計模式,為什麼要使用單例設計模式,如何實作單例設計模式(餓漢式和懶漢式)?

什麼是單例設計模式?

單例設計模式就是一種控制執行個體化對象個數的設計模式。

為什麼要使用單例設計模式?

使用單例設計模式可以節省記憶體空間,提高性能。因為很多情況下,有些類是不需要重複産生對象的。

如果重複産生對象的話,會導緻大量的記憶體空間被占用,性能降低。

例如:在程式啟動中,加載已儲存的資料資訊。這些資料資訊是由一個單例對象統一讀取,其他程式隻需要通過這個單例對象擷取加載的資料資訊即可。

單例設計模式分為餓漢式和懶漢式。餓漢式是在系統加載類的時候就會自動提供類的執行個體化對象如Computer computer。

懶漢式是在第一次使用的時候進行執行個體化對象處理。

餓漢式單例設計模式實作源碼:

class Computer{

  //1、私有化 Computer 構造函數
  private Computer(){
    System.out.println("私有化 Computer 構造函數");
  }
  public void printInfo(){
    System.out.println("Computer 類使用的就是單例設計模式");
  }

  // 2、調用私有化 Computer 構造函數并将computer屬性設定為static
  private static Computer computer = new Computer();

  // 3、提供getCompter()方法,便于調用
  public static Computer getCompter(){
    return computer;
  }
}

public class Demo {

  public static void main(String[] args) {
    // 産生單例對象
    Computer computer = Computer.getCompter();

    // 調用printInfo()方法
    computer.printInfo();
  }
}           

運作結果:

私有化 Computer 構造函數
Computer 類使用的就是單例設計模式           

餓漢式單例設計模式的實作過程分析:

1、所有新的執行個體化對象的産生都會調用構造方法,如果無法正常調用構造方法的話,也就不能産生新的執行個體化對象。

如果想控制執行個體化對象個數的話,那麼就應該控制構造函數。是以首先将該類的構造方法定義為私有方法。

private Computer(){
    System.out.println("私有化Computer 構造函數");
  }           

2、類的構造方法私有化後,在類的外部就不能産生執行個體化對象。但是Private 修飾的構造方法可以在類的内部通路。

如果要通路Private 修飾的構造方法,可以在類的内部調用構造函數。

private Computer computer = new Computer();           

3、computer 作為普通屬性,隻有在執行個體化對象産生之後才能被調用。由于類的外部無法産生執行個體化對象,如果想擷取

computer 屬性,可以将computer 屬性設定為static。

private static Computer computer = new Computer();           

4、對于private 屬性 computer來說,如果想在類的外部擷取該屬性,則需要通過getComputer()方法擷取。

public static Computer getCompter(){
    return computer;
  }           

由于餓漢式在類加載的時候就完成了對象執行個體化,如果程式始終沒有用到這個執行個體化對象,那麼就會造成記憶體空間的浪費。

為了不浪費記憶體空間,懶漢式是在第一次使用的時候進行執行個體化對象處理。

懶漢式單例設計模式實作源碼:

class Computer{

  //1、私有化 Computer 構造函數
  private Computer(){
    System.out.println("私有化 Computer 構造函數");
  }
  public void printInfo(){
    System.out.println("Computer 類使用的就是單例設計模式");
  }

  // 2、調用私有化 Computer 構造函數并将computer屬性設定為static
  private static Computer computer;

  // 3、提供getCompter()方法,便于調用
  public static Computer getCompter(){

    // 懶漢式,按需建立 即在第一次使用的時候進行執行個體化對象
    if(computer == null){
      computer = new Computer();
    }
    return computer;
  }
}

public class Demo {
  public static void main(String[] args) {
    // 産生單例對象
    Computer computer = Computer.getCompter();

    // 調用printInfo()方法
    computer.printInfo();
  }
}           
私有化 Computer 構造函數
Computer 類使用的就是單例設計模式           

懶漢式單例設計模式的實作源碼分析:

為了避免執行個體化的對象始終沒有被使用,造成記憶體空間的浪費,是以增加了對執行個體化對象的判斷,

即如果執行個體化對象為null 則建立執行個體化對象。

// 懶漢式,按需建立 即隻有在第一次使用的時候才進行執行個體化對象
    if(computer == null){
      computer = new Computer();
    }           

但是如果在多線程下,會出現這樣的情況即一個線程進入了if 語句, 另一個線程也通過了if語句。

這樣就産生了多個執行個體化對象。 為了避免這樣的問題,可以采用雙重加鎖機制。

雙重加鎖機制優化懶漢式源碼:

class Computer{
  //1、私有化 Computer 構造函數
  private Computer(){
    System.out.println("私有化 Computer 構造函數");
  }

  public void printInfo(){
    System.out.println("Computer 類使用的就是單例設計模式");
  }

  // 2、調用私有化 Computer 構造函數并将computer屬性設定為static
  private volatile static Computer computer;

  // 3、提供getCompter()方法,便于調用
  public static Computer getCompter(){
    // 第一次檢查
    if(computer == null){
      // 加鎖
      synchronized (Computer.class){
        // 第二次檢查
        if (computer == null){
          computer = new Computer();
        }
      }
    }
    return computer;
  }
}

public class Demo {
  public static void main(String[] args) {
    // 産生單例對象
    Computer computer = Computer.getCompter();

    // 調用printInfo()方法
    computer.printInfo();
  }
}
           
私有化 Computer 構造函數
Computer 類使用的就是單例設計模式           

雙重加鎖機制優化懶漢式源碼分析:

// 2、調用私有化 Computer 構造函數并将computer屬性設定為static
  private volatile static Computer computer;

  // 3、提供getCompter()方法,便于調用
  public static Computer getCompter(){
    // 第一次檢查
    if(computer == null){
      // 加鎖
      synchronized (Computer.class){
        // 第二次檢查
        if (computer == null){
          computer = new Computer();
        }
      }
    }
    return computer;
  }           

1、volatile可以保證多線程下的可見性即保證了子線程的會跟主線程的一緻。

2、當thread2,進入第一個if(computer == null) 語句,子線程的computer為空的,thread2釋放資源給thread3。

3、當thread3,進入第一個if(computer == null) 語句,子線程的computer為空的,thread3釋放資源給thread2。

4、當thread2,進入第二個if(computer == null) 語句,執行computer = new Computer(),執行個體化對象computer,volatile修飾的變量computer,會馬上同步到主線程的變量computer,執行完成後thread2釋放資源給thread3。

5、當thread3,進入第二個if (computer == null) 語句,此時子線程的computer不為空,是以thread3不再會重複執行個體化computer。