傳統的兩私有一公開(私有構造方法、私有靜態實例(懶實例化/直接實例化)、公開的靜態獲取方法)涉及線程安全問題(即使有多重檢查鎖也可以通過反射破壞單例),
目前最為安全的實現單例的方法是通過內部靜態enum的方法來實現,因為JVM會保證enum不能被反射並且構造器方法只執行一次。
實現方法如下:
/** * 使用枚舉的單例模式 * * @author yzl * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class EnumSingleton{ private EnumSingleton(){} public static EnumSingleton getInstance(){ return Singleton.INSTANCE.getInstance(); } private static enum Singleton{ INSTANCE; private EnumSingleton singleton; //JVM會保證此方法絕對只調用一次 private Singleton(){ singleton = new EnumSingleton(); } public EnumSingleton getInstance(){ return singleton; } } }
測試方法:
public static void main(String[] args) { EnumSingleton obj1 = EnumSingleton.getInstance(); EnumSingleton obj2 = EnumSingleton.getInstance(); //輸出結果:obj1==obj2?true System.out.println("obj1==obj2?" + (obj1==obj2)); }
擴展應用,觀察下面的例子
public class Test{ //初始化一些東西 static{ } }
這是一個很常見的類內部的靜態資源初始化的寫法(其實也就是單例的另外一種表現-只執行一次),但是將代碼都寫在static塊下會看起來很不優雅,可以利用上面的enum單例模式來進行初始化操作。
見例子:
import java.util.ArrayList; import java.util.List; /** * 初始化的優雅實現 * 可以在static處調用, * 也可以在普通方法里調用,都保證只初始化一次 * * 當然將enum塊的代碼直接放到StaticInitTest類的private static 方法里做也是可以的 * * @author yzl * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ public class StaticInitTest { private static List<Integer> dataList = null; static{ dataList = Singleton.INSTANCE.init(); } /** * * 單例模式來填充數據 * * @author yzl * @see [相關類/方法](可選) * @since [產品/模塊版本] (可選) */ private static enum Singleton { INSTANCE; private List<Integer> list; private Singleton(){ fillData(); } /** * * 初始化數據 * * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ private void fillData(){ list = new ArrayList<Integer>(5); for(int i =1; i<6; i++){ list.add(i); } } /** * * 初始化的入口 * * @see [相關類/方法](可選) * @since [產品/模塊版本](可選) */ public List<Integer> init(){ return list; } } }