單例模式,顧名思義就是單個實例,就是說一個類有且僅有一個實例,應該是23中設計模式中最簡單也最好理解的設計模式了,同時簡單的往往也是重要的和常用的,對吧?簡單的往往也是常用的,這句話在任何地方都能適用。
一、常見的幾種寫法
1、餓漢式(可以使用)
特點:線程安全的,不能實現延遲加載
public class HungryMan {
/**1、私有化構造器:所有的單例模式第一步永遠都是私有化構造器,因爲只有不讓在外面調用構造器new
* 對象了,才能控制對象的數量,這是單例模式的前提
*/
private HungryMan(){}
//2、內的內部創建出實例
private static final HungryMan HUNGRY_MAN = new HungryMan();
//3、提供公共靜態的方法返回實例
public static HungryMan getInstance(){
return HUNGRY_MAN;
}
}
1.1餓漢式之靜態代碼塊版(可以使用)
特點:線程安全的,不能實現延遲加載
public class HungryMan1 {
private HungryMan1(){}
private static HungryMan1 hungryMan1;
static {
hungryMan1 = new HungryMan1();
}
public static HungryMan1 getInstance(){
return hungryMan1;
}
}
2、懶漢式(不推薦使用)
特點:不是線程安全的,能延遲加載
public class LazyMan {
private LazyMan(){}
private static LazyMan lazyMan;
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
2.1懶漢式之同步方法版(可以使用)
特點:線程安全的,能延遲加載,但效率低(當有多個線程同時調用時,不管有沒有實例都要等待拿鎖)
public class LazyMan1 {
private LazyMan1(){}
private static LazyMan1 lazyMan1;
public static synchronized LazyMan1 getInstance(){
if (lazyMan1 == null) {
lazyMan1 = new LazyMan1();
}
return lazyMan1;
}
}
2.2懶漢式之同步代碼塊版(可以使用)
特點:線程安全的,能延遲加載,但效率低
public class LazyMan2 {
private LazyMan2(){}
private static LazyMan2 lazyMan2;
public static LazyMan2 getInstance(){
synchronized (LazyMan2.class){
if (lazyMan2 == null) {
lazyMan2 = new LazyMan2();
}
return lazyMan2;
}
}
}
2.3懶漢式之雙重檢查版(較推薦使用)
特點:線程安全的,能延遲加載,效率高(據說雙重檢查有線程安全問題,不確定還)
public class LazyMan3 {
private LazyMan3(){}
private static LazyMan3 lazyMan3;
public static LazyMan3 getInstance(){
if (lazyMan3 == null){
synchronized (LazyMan3.class){
if (lazyMan3 == null) {
lazyMan3 = new LazyMan3();
}
}
}
return lazyMan3;
}
}
3、靜態內部類版(推薦使用)
特點:線程安全,能延遲加載,效率高
public class StaticInsideClass {
private StaticInsideClass(){}
//在靜態內部類中創建外部類的實例
//PS:靜態內部類會等到它被調用的時候才加載
private static class InnerClass{
private static final StaticInsideClass STATIC_INSIDE_CLASS = new StaticInsideClass();
}
public static StaticInsideClass getInstance(){
return InnerClass.STATIC_INSIDE_CLASS;
}
}
4、枚舉類版(推薦使用)
特點:線程安全的,效率高,能避免反射和反序列化創建對象的問題,不能延遲加載
public enum EnumSingleton {
//被枚舉出來的對象
Instance;
/*當前枚舉類的其他屬性和方法*/
//名字
private String name;
//年齡
private Integer age;
public void doSomeThing(){
System.out.println("當前枚舉出來的對象做一些事情~~~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
5、登記式:用一個線程安全的Map來保存對象實例。在此就不寫了,大家可以自己實現一下。
二、常見的應用場景
首先,想一下單例模式的特點:在一個系統中只有一個對象實例,我們可以好好設計這個類,比如設計一些讀取和寫入的方法,來對類中的屬性(比如集合等)進行操作,因爲就只有一個對象,該對象擁有自己的一套屬性和方法,即有自己的內存空間,這樣該類就能成爲系統運行期間的一個小的內存數據庫。(當前要保存該類不會被JVM當成垃圾回收了纔行)
1、系統配置文件的讀取時,對應一個單例的類。
2、網站計數器來記錄網站訪問的次數、在線人數等
3、臨時對象的頻繁創建和銷燬時,比如需要創建一個對象來保存數據,然後進行插入數據庫操作時。
4、各種池的創建都是單例模式的,比如數據庫連接池、線程池等。
總之一句話,凡是需要保證一份資源只能有一份時,就用單例模式。
三、問題
這裏我有個問題先記下來:爲什麼單例模式的對象不會被JVM當成垃圾回收?
答: 等我瞭解完JVM的知識後再來填坑吧
聰明的小夥伴看到後,能否在我填坑前,在評論區留言解答呢?說出你的看法,show time~~