題目:設計一個類,我們只能生成該類的一個實例。
解法如下:
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());
}
}