1. 程式人生 > >被破壞的單例模式

被破壞的單例模式

被破壞的單例模式

我們知道餓漢單例天生是執行緒安全的,但是通過其他途徑單例模式是可以被破壞的。懶漢亦如此。


1、通過反射來破解單例模式

	public class Eagersingleton implements Serializable {

		private static final long serialVersionUID = 888L;
		private static Eagersingleton m_instance = new Eagersingleton();// 初始化時已經自行例項化

		// 私有預設構造方法
		private Eagersingleton() {
		}

		// 靜態工廠方法
		public static Eagersingleton getInstance() {
			return m_instance;
		}
	}
	public class Test1 {

		public static void main(String[] args) throws NoSuchMethodException,
				SecurityException {
			// 獲取單例物件
			Eagersingleton instance = Eagersingleton.getInstance();
			Eagersingleton instance2 = null;
			try {
				Class cls = Eagersingleton.class;
				Constructor constructor = cls.getDeclaredConstructor(null);
				// 關掉安全檢查,可以呼叫私有構造器
				constructor.setAccessible(true);
				// 通過反射得到一個物件
				instance2 = (Eagersingleton) constructor.newInstance(null);
			} catch (InstantiationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(instance);
			System.out.println(instance2);
		}
	}
    
打印出來的結果不一樣,說明這兩個物件就是不同的物件,這樣就破解了單例模式。

解決方案:避免反射


反射是通過它的Class物件來呼叫構造器創建出新的物件,我們只需要在構造器中手動丟擲異常,導致程式停止就可以達到目的了。

2、通過序列化和反序列化破解單例

	public class Test2 {

		public static void main(String[] args) throws FileNotFoundException,
				ClassNotFoundException {
			// 獲取單例物件
			Eagersingleton instance = Eagersingleton.getInstance();

			// 通過反序列化讀取物件
			Eagersingleton instance2 = null;
			try {
				ObjectOutputStream oossStream = new ObjectOutputStream(
						new FileOutputStream("D:/EagersingletonTest.txt"));

				// 通過序列化把物件寫到檔案中
				oossStream.writeObject(instance);
				oossStream.flush();
				oossStream.close();
				// 讀取檔案的物件
				ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
						"D:/EagersingletonTest.txt"));

				instance2 = (Eagersingleton) ois.readObject();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			System.out.println(instance);
			System.out.println(instance2);
		}
	}

打印出來的結果不一樣,說明這兩個物件就是不同的物件,破解了單例模式。

解決方案:避免序列化

readResolve()這個方法是基於回撥的,反序列化時,如果定義了readResolve()則直接返回此方法指定的物件,而不需要在建立新的物件。









每天努力一點,每天都在進步。