設計模式2——單例模式
阿新 • • 發佈:2020-08-26
設計模式2——單例模式
餓漢式:
package com.ghl.single; /** * @ProjectName DesignPattern * @ClassName Hungry * @Date 2020/8/25 15:42 * @Author gaohengli * @Version 1.0 */ //餓漢式單例 public class Hungry { //可能會浪費空間 private byte[] data1=new byte[1024*1024]; private byte[] data2=new byte[1024*1024]; private byte[] data3=new byte[1024*1024]; private byte[] data4=new byte[1024*1024]; //私有化建構函式 private Hungry() { } //設立靜態變數,直接建立例項 private static final Hungry HUNGRY = new Hungry(); //開放公有方法,判斷是否已存在例項 public static Hungry getInstance(){ return HUNGRY; } }
懶漢式:
package com.ghl.single; import java.lang.reflect.Constructor; import java.lang.reflect.Field; /** * @ProjectName DesignPattern * @ClassName LazyMan * @Date 2020/8/25 16:01 * @Author gaohengli * @Version 1.0 */ //DCL懶漢式單例 public class LazyMan { private static boolean flag = false; //私有化構造器 private LazyMan() { /*//解決反射破壞單例的問題 synchronized (LazyMan.class) { if (lazyMan != null) { throw new RuntimeException("不要試圖用反射破壞異常"); } }*/ synchronized (LazyMan.class) { if (flag==false){ flag=true; }else { throw new RuntimeException("不要試圖用反射破壞異常"); } } } //volatile可見性,禁止指令重排序 private volatile static LazyMan lazyMan; //雙重檢測鎖模式的懶漢式單例,DCL懶漢式 public static LazyMan getInstance() { if (lazyMan == null) { synchronized (LazyMan.class) { if (lazyMan == null) { lazyMan = new LazyMan();//不是一個原子性操作 /* 1.分配記憶體空間 2.執行構造方法,初始化物件 3.把這個物件執行這個空間 */ } } } return lazyMan; } /*//多執行緒併發 public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } }*/ //反射,會破壞單例 public static void main(String[] args) throws Exception { //LazyMan instance = LazyMan.getInstance(); Field flag = LazyMan.class.getDeclaredField("flag"); flag.setAccessible(true); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true);//破壞私有許可權 LazyMan instance = declaredConstructor.newInstance(); flag.set(instance,false); LazyMan instance1 = declaredConstructor.newInstance(); System.out.println(instance); System.out.println(instance1); } }
靜態內部類:
package com.ghl.single; /** * @ProjectName DesignPattern * @ClassName Holder * @Date 2020/8/25 17:10 * @Author gaohengli * @Version 1.0 */ //靜態內部類實現 public class Holder { private Holder(){ } public static Holder getInstance(){ return InnerClass.HOLDER; } //靜態內部類 public static class InnerClass{ private static final Holder HOLDER=new Holder(); } }
*前面的單例模式都不安全,因為反射可以破解,因此下面使用列舉。
列舉單例模式:
package com.ghl.single;
import java.lang.reflect.Constructor;
/**
* @ProjectName DesignPattern
* @ClassName EnumSingle
* @Date 2020/8/25 17:35
* @Author gaohengli
* @Version 1.0
*/
//enum,列舉,本身也是一個Class類
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
class Test{
public static void main(String[] args) throws Exception {
EnumSingle instance = EnumSingle.INSTANCE;
//Exception in thread "main" java.lang.NoSuchMethodException: com.ghl.single.EnumSingle.<init>()
//Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance1 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance1);
}
}
檢測列舉時結果會報Exception in thread "main" java.lang.NoSuchMethodException。出現這個異常的原因是因為EnumSingleton.class.getDeclaredConstructors()獲取所有構造器,會發現並沒有我們所設定的無參構造器,只有一個引數為(String.class,int.class)構造器,而且在反射在通過newInstance建立物件時,會檢查該類是否ENUM修飾,如果是則丟擲異常,反射失敗。所以列舉是不怕發射攻擊的。
列舉有有參建構函式。列舉不怕反射的攻擊。