單例模式的實現(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方法的結果:
添加readResolve方法的結果:

感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
新聞熱點
疑難解答