Java實現單例模式之餓漢式、懶漢式、枚舉式_java

來源:互聯網
上載者:User

單例模式的實現(5種)

常用:

餓漢式(安全執行緒,調用效率高,但是不能延時載入)
懶漢式(安全執行緒,調用效率不高,可以延時載入)

其他:

雙重檢測鎖式(由於jvm底層內部模型原因,偶爾會出問題,不建立使用)
靜態內部類式(安全執行緒,調用效率高,但是可以延時載入)
枚舉單例(安全執行緒,調用效率高,不能延時載入)
餓漢式單例具體代碼如下:

package com.lcx.mode;   /**  *  * 餓漢式單例,不管以後用不用這個對象,我們一開始就建立這個對象的執行個體,  * 需要的時候就返回已建立好的執行個體對象,所以比較饑餓,故此叫餓漢式單例。  * @author qq1013985957  *  */ public class SingletonHanger {   private static final SingletonHanger instance = new SingletonHanger();   private SingletonHanger() {   }   public static SingletonHanger getInstance(){     return instance;   } } /**  * 懶漢漢式單例,在需要單例對象的時候,才建立唯一的單例對象,以後再次調用,返回的也是第一建立的單例對象  * 將靜態成員初始化為null,在擷取單例的時候才建立,故此叫懶漢式。  * @author qq1013985957  *  */ class SingletonLazy{   private static SingletonLazy instance = null;   private SingletonLazy() {   }   /**    * 此方法實現的單例,無法在多線程中使用,多線可以同時進入if方法,會導致產生多個單例對象。    * @return    */   public static SingletonLazy getInstance1(){     if(instance==null){       instance = new SingletonLazy();     }     return instance;   }   /**    * 大家都會想到同步,可以同步方法實現多線程的單例    * 但是這種方法不可取,嚴重影響效能,因為每次去取單例都要檢查方法,所以只能用同步代碼塊的方式實現同步。    * @return    */   public static synchronized SingletonLazy getInstance2(){     if(instance==null){       instance = new SingletonLazy();     }     return instance;   }   /**    * 用同步代碼塊的方式,在判斷單例是否存在的if方法裡使用同步代碼塊,在同步代碼塊中再次檢查是否單例已經產生,    * 這也就是網上說的 雙重檢查加鎖的方法    * @return    */   public static synchronized SingletonLazy getInstance3(){     if(instance==null){       synchronized (SingletonLazy.class) {         if(instance==null){           instance = new SingletonLazy();         }       }     }     return instance;   } } /**  *  * 使用枚舉實現單例模式,也是Effective Java中推薦使用的方式  * 根據具體情況進行執行個體化,對枚舉不熟悉的同學,可以參考我的部落格 JAVA 枚舉類的初步理解。  * 它的好處:更加簡潔,無償提供了序列化機制,絕對防止多次執行個體化,即使面對複雜的序列和反射攻擊。  * @author qq1013985957  *  */ enum SingletionEnum{   SingletionEnum("單例的枚舉方式");   private String str ;   private SingletionEnum(String str){     this.setStr(str);   }   public String getStr() {     return str;   }   public void setStr(String str) {     this.str = str;   }    } 

以上的單例模式就不測試,大家可以去測試,判斷對象的hashcode是否一致來判斷是否為同一個對象。

惡漢式、懶漢式的方式還不能防止反射來實現多個執行個體,通過反射的方式,設定ACcessible.setAccessible方法可以調用私人的構造器,可以修改構造器,讓它在被要求建立第二個執行個體的時候拋出異常。

其實這樣還不能保證單例,當序列化後,還原序列化是還可以建立一個新的執行個體,在單例類中添加readResolve()方法進行防止。
懶漢漢式單例代碼如下:

package com.lcx.mode;  import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException;  /**  * 懶漢漢式單例,在需要單例對象的時候,才建立唯一的單例對象,以後再次調用,返回的也是第一建立的單例對象  * 將靜態成員初始化為null,在擷取單例的時候才建立,故此叫懶漢式。  * @author qq1013985957  *  */ public class Singleton implements Serializable{   /**    *    */   private static final long serialVersionUID = -5271537207137321645L;   private static Singleton instance = null;   private static int i = 1;   private Singleton() {     /**      * 防止反射攻擊,只運行調用一次構造器,第二次拋異常      */     if(i==1){       i++;     }else{       throw new RuntimeException("只能調用一次建構函式");     }     System.out.println("調用Singleton的私人構造器");        }   /**    * 用同步代碼塊的方式,在判斷單例是否存在的if方法裡使用同步代碼塊,在同步代碼塊中再次檢查是否單例已經產生,    * 這也就是網上說的 雙重檢查加鎖的方法    * @return    */   public static synchronized Singleton getInstance(){     if(instance==null){       synchronized (Singleton.class) {         if(instance==null){           instance = new Singleton();         }       }     }     return instance;   }   /**    *    * 防止反序列產生新的單例對象,這是effective Java 一書中說的用此方法可以防止,具體細節我也不明白    * @return    */   private Object readResolve(){     return instance;   }   public static void main(String[] args) throws Exception {     test1();     test2();   }   /**    * 測試 反序列 仍然為單例模式    * @throws Exception    */   public static void test2() throws Exception{     Singleton s = Singleton.getInstance();     ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("E:\\Singleton.txt")));     objectOutputStream.writeObject(s);     ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("E:\\Singleton.txt")));     Object readObject = objectInputStream.readObject();     Singleton s1 = (Singleton)readObject;     System.out.println("s.hashCode():"+s.hashCode()+",s1.hashCode():"+s1.hashCode());          objectOutputStream.flush();     objectOutputStream.close();     objectInputStream.close();   }   /**    * 測試反射攻擊    * @throws Exception    */   public static void test1(){     Singleton s = Singleton.getInstance();     Class c = Singleton.class;     Constructor privateConstructor;     try {       privateConstructor = c.getDeclaredConstructor();       privateConstructor.setAccessible(true);       privateConstructor.newInstance();     } catch (Exception e) {       e.printStackTrace();     }   } } 

驗證反射攻擊結果:

如果不添加readResolve方法的結果:

添加readResolve方法的結果:

感謝閱讀,希望能協助到大家,謝謝大家對本站的支援!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.