劍指offer-chapter2-面試題2-單例模式(java)

題目:設計一個類,我們只能生成該類的一個實例。

解法如下:

package problem2;

/**
 * 問題:設計一個類,我們只能生成該類的一個實例。
 * Created by fengyuwusong on 2018/1/15 15:04.
 */

/**
 * 懶漢式加載
 * 只適合單線程環境(不好)
 * 註解:Singleton的靜態屬性instance中,只有instance爲null的時候才創建一個實例,構造函數私有,確保每次都只創建一個,避免重複創建。
 * 缺點:只在單線程的情況下正常運行,在多線程的情況下,就會出問題。例如:當兩個線程同時運行到判斷instance是否爲空的if語句,
 * 並且instance確實沒有創建好時,那麼兩個線程都會創建一個實例。
 */
class Singleton1 {
    private Singleton1() {
    }

    private static Singleton1 instance = null;

    public static Singleton1 getInstance() {
        if (instance == null) {
            instance = new Singleton1();
        }
        return instance;
    }
}

/**
 * 改進懶漢式,多線程情況下可用(效率不好)
 * 註解:在解法一的基礎上加上了同步鎖,使得在多線程的情況下可以用。例如:當兩個線程同時想創建實例,由於在一個時刻只有一個線程能得到同步鎖,
 * 當第一個線程加上鎖以後,第二個線程只能等待。第一個線程發現實例沒有創建,創建之。第一個線程釋放同步鎖,第二個線程纔可以加上同步鎖,執行下面的代碼。
 * 由於第一個線程已經創建了實例,所以第二個線程不需要創建實例。保證在多線程的環境下也只有一個實例。
 * 缺點:每次通過getInstance方法得到singleton實例的時候都有一個試圖去獲取同步鎖的過程。而衆所周知,加鎖是很耗時的。能避免則避免。
 */

class Singleton2 {
    private Singleton2() {
    }

    private static Singleton2 instance = null;

    public static synchronized Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }
}

/**
 * 加同步鎖時,前後兩次判斷實例是否存在(可行)
 * 註解:只有當instance爲null時,需要獲取同步鎖,創建一次實例。當實例被創建,則無需試圖加鎖。
 * 缺點:用雙重if判斷,複雜,容易出錯。
 */
class Singleton3 {
    private Singleton3() {
    }

    private volatile static Singleton3 instance = null;

    public static Singleton3 getInstance() {
        if (instance == null) {
            synchronized (Singleton3.class) {
                if (instance == null) {
                    instance = new Singleton3();
                }
            }
        }
        return instance;
    }
}

/**
 * 餓漢式
 * 註解:初試化靜態的instance創建一次。如果我們在Singleton類裏面寫一個靜態的方法不需要創建實例,它仍然會早早的創建一次實例。而降低內存的使用率。
 * 缺點:沒有lazy loading的效果,從而降低內存的使用率。
 */
class Singleton4 {
    private Singleton4() {
    }

    public static Singleton4 instance = new Singleton4();

    public static Singleton4 getInstance() {
        return instance;
    }
}

/**
 * 靜態內部
 * 註解:定義一個私有的內部類,在第一次用這個嵌套類時,會創建一個實例。而類型爲SingletonHolder的類,只有在Singleton.getInstance()中調用,
 * 由於私有的屬性,他人無法使用SingleHolder,不調用Singleton.getInstance()就不會創建實例。
 * 優點:達到了lazy loading的效果,即按需創建實例。
 */
class Singleton5 {
    private Singleton5() {
    }

    private static class SingletonHolder {
        private final static Singleton5 instance = new Singleton5();
    }

    public static Singleton5 getInstance() {
        return SingletonHolder.instance;
    }
}

/**
 * effect java中推薦寫法
 * 優點
 * (1)自由序列化。
   (2)保證只有一個實例。
   (3)線程安全。
 */
enum Singleton6{
    INSTANCE;
    public void otherMethods(){
        System.out.println("單例裏面的方法~");
    }
}

拓展問題: 定義一個表示總統的類型President,可以從改類型繼承出FrechPresident和AmericanPresident, 這些派生類只能產生一個實例。

解法如下:

package problem2;

/**
 * 問題拓展: 定義一個表示總統的類型President,可以從改類型繼承出FrechPresident和AmericanPresident,
 * 這些派生類只能產生一個實例。
 * Created by fengyuwusong on 2018/1/15 15:42.
 */
class President {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


class FrenchPresident extends President {
    private FrenchPresident() {
    }

    private static class Nested {
        private final static FrenchPresident instance = new FrenchPresident();
    }

    public static FrenchPresident getInstance() {
        return Nested.instance;
    }

    public static void main(String[] args) {
        FrenchPresident s1 = FrenchPresident.getInstance();
        FrenchPresident s2 = FrenchPresident.getInstance();

        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

    }
}

class AmericanPresident {
    private AmericanPresident() {
    }

    public static AmericanPresident getInstance() {
        return Nested.instance;
    }

    private static class Nested {

        public static final AmericanPresident instance = new AmericanPresident();
    }

    public static void main(String[] args) {
        AmericanPresident s1 = AmericanPresident.getInstance();
        AmericanPresident s2 = AmericanPresident.getInstance();

        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章