一、 Java中的反射技術可以獲取類的所有方法、成員變量、還能訪問private的構造方法,這樣一來,單例模式中用的私有構造函數被調用就會產生多個實例,編寫代碼測試一下。
- package test;
- import java.lang.reflect.Constructor;
- public class SingetonTest {
- private static SingetonTest singleton = null;
- private int s = 0;
- // 構造方法是私有的
- private SingetonTest(){}
- // 同步的獲取實例方法
- public static synchronized SingetonTest getInstance(){
- // 懶漢模式的單例方法
- if(null == singleton){
- singleton = new SingetonTest();
- }
- return singleton;
- }
- public int getS() {
- return s;
- }
- public void setS(int s) {
- this.s = s;
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- try {
- Constructor con = SingetonTest.class.getDeclaredConstructor();
- con.setAccessible(true); //設置是否是可訪問的
- // 通過反射獲取實例
- SingetonTest singetonTest1 = (SingetonTest)con.newInstance();
- SingetonTest singetonTest2 = (SingetonTest)con.newInstance();
- // 常規方法獲取實例
- SingetonTest singetonTest3 = SingetonTest.getInstance();
- SingetonTest singetonTest4 = SingetonTest.getInstance();
- // 測試輸出
- System.out.println("singetonTest1.equals(singetonTest2) :" + singetonTest1.equals(singetonTest2));
- System.out.println("singetonTest3.equals(singetonTest4) :" + singetonTest3.equals(singetonTest4));
- System.out.println("singetonTest1.equals(singetonTest3) :" + singetonTest1.equals(singetonTest3));
- singetonTest1.setS(1);
- singetonTest2.setS(2);
- singetonTest3.setS(3);
- singetonTest4.setS(4);
- System.out.println("1:" + singetonTest1.getS() + " 2:" + singetonTest2.getS()+ " 3:" + singetonTest3.getS()+ " 4:" + singetonTest4.getS());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
package test;
import java.lang.reflect.Constructor;
public class SingetonTest {
private static SingetonTest singleton = null;
private int s = 0;
// 構造方法是私有的
private SingetonTest(){}
// 同步的獲取實例方法
public static synchronized SingetonTest getInstance(){
// 懶漢模式的單例方法
if(null == singleton){
singleton = new SingetonTest();
}
return singleton;
}
public int getS() {
return s;
}
public void setS(int s) {
this.s = s;
}
/**
* @param args
*/
public static void main(String[] args) {
try {
Constructor con = SingetonTest.class.getDeclaredConstructor();
con.setAccessible(true);
// 通過反射獲取實例
SingetonTest singetonTest1 = (SingetonTest)con.newInstance();
SingetonTest singetonTest2 = (SingetonTest)con.newInstance();
// 常規方法獲取實例
SingetonTest singetonTest3 = SingetonTest.getInstance();
SingetonTest singetonTest4 = SingetonTest.getInstance();
// 測試輸出
System.out.println("singetonTest1.equals(singetonTest2) :" + singetonTest1.equals(singetonTest2));
System.out.println("singetonTest3.equals(singetonTest4) :" + singetonTest3.equals(singetonTest4));
System.out.println("singetonTest1.equals(singetonTest3) :" + singetonTest1.equals(singetonTest3));
singetonTest1.setS(1);
singetonTest2.setS(2);
singetonTest3.setS(3);
singetonTest4.setS(4);
System.out.println("1:" + singetonTest1.getS() + " 2:" + singetonTest2.getS()+ " 3:" + singetonTest3.getS()+ " 4:" + singetonTest4.getS());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
測試結果:
singetonTest1.equals(singetonTest2) :false
singetonTest3.equals(singetonTest4) :true
singetonTest1.equals(singetonTest3) :false
1:1 2:2 3:4 4:4
通過反射技術生成的兩個實例不同,通過常規方法獲取的兩個實例相同(即同一個實例,單例)。
二、防止反射破壞單例模式,構造函數調用時進行處理,當構造函數第2次被調用時拋出異常!修改構造方法如下:
- private static boolean flag = false;
- // 構造方法是私有的
- private SingetonTest(){
- if(flag){
- flag = !flag;
- }
- else{
- try {
- throw new Exception("duplicate instance create error!" + SingetonTest.class.getName());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
private static boolean flag = false;
// 構造方法是私有的
private SingetonTest(){
if(flag){
flag = !flag;
}
else{
try {
throw new Exception("duplicate instance create error!" + SingetonTest.class.getName());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
三、一些思考
1)單例模式是爲了保證一個類只有一個實例,整個系統只能有自己創建的一個實例。應用在數據庫連接或單個隊列處理等問題。
2)單例模式構造,懶漢模式以時間換空間(用的時候才生產實例,每次都判斷);懶漢模式以空間換時間,直接創建實例,以後不用判斷直接用。
3)懶漢模式線程安全問題,獲取實例的方法要加上synchronized進行同步。