zoukankan      html  css  js  c++  java
  • Java反射破坏单例模式

    今天电话面试的时候问到了,Google了一下

    原出处:

    http://blog.csdn.net/lws332969674/article/details/8125893

    一、 Java中的反射技术可以获取类的所有方法、成员变量、还能访问private的构造方法,这样一来,单例模式中用的私有构造函数被调用就会产生多个实例,编写代码测试一下。

    1. package test;  
    2.   
    3. import java.lang.reflect.Constructor;  
    4.   
    5. public class SingetonTest {  
    6.   
    7.     private static SingetonTest singleton = null;  
    8.     private int s = 0;  
    9.       
    10.     // 构造方法是私有的  
    11.     private SingetonTest(){}  
    12.       
    13.     // 同步的获取实例方法  
    14.     public static synchronized SingetonTest getInstance(){  
    15.         // 懒汉模式的单例方法  
    16.         if(null == singleton){  
    17.             singleton = new SingetonTest();  
    18.         }  
    19.         return singleton;  
    20.     }  
    21.       
    22.       
    23.     public int getS() {  
    24.         return s;  
    25.     }  
    26.   
    27.     public void setS(int s) {  
    28.         this.s = s;  
    29.     }  
    30.   
    31.     /** 
    32.      * @param args 
    33.      */  
    34.     public static void main(String[] args) {  
    35.         try {  
    36.             Constructor con = SingetonTest.class.getDeclaredConstructor();  
    37.             con.setAccessible(true);  
    38.             // 通过反射获取实例  
    39.             SingetonTest singetonTest1 = (SingetonTest)con.newInstance();  
    40.             SingetonTest singetonTest2 = (SingetonTest)con.newInstance();  
    41.             // 常规方法获取实例  
    42.             SingetonTest singetonTest3 = SingetonTest.getInstance();  
    43.             SingetonTest singetonTest4 = SingetonTest.getInstance();  
    44.             // 测试输出  
    45.             System.out.println("singetonTest1.equals(singetonTest2) :" +  singetonTest1.equals(singetonTest2));  
    46.             System.out.println("singetonTest3.equals(singetonTest4) :" +  singetonTest3.equals(singetonTest4));  
    47.             System.out.println("singetonTest1.equals(singetonTest3) :" +  singetonTest1.equals(singetonTest3));  
    48.             singetonTest1.setS(1);  
    49.             singetonTest2.setS(2);  
    50.             singetonTest3.setS(3);  
    51.             singetonTest4.setS(4);  
    52.             System.out.println("1:" + singetonTest1.getS() + "  2:" + singetonTest2.getS()+ "  3:" + singetonTest3.getS()+ "  4:" + singetonTest4.getS());  
    53.               
    54.         } catch (Exception e) {  
    55.             // TODO Auto-generated catch block  
    56.             e.printStackTrace();  
    57.         }  
    58.   
    59.     }  
    60.   
    61. }  

    测试结果:

    singetonTest1.equals(singetonTest2) :false
    singetonTest3.equals(singetonTest4) :true
    singetonTest1.equals(singetonTest3) :false
    1:1  2:2  3:4  4:4

    通过反射技术生成的两个实例不同,通过常规方法获取的两个实例相同(即同一个实例,单例)。


    二、防止反射破坏单例模式,构造函数调用时进行处理,当构造函数第2次被调用时抛出异常!修改构造方法如下:

    1. private static boolean flag = false;  
    2.       
    3.     // 构造方法是私有的  
    4.     private SingetonTest(){  
    5.         if(flag){  
    6.             flag = !flag;  
    7.         }  
    8.         else{  
    9.             try {  
    10.                 throw new Exception("duplicate instance create error!" + SingetonTest.class.getName());  
    11.             } catch (Exception e) {  
    12.                 // TODO Auto-generated catch block  
    13.                 e.printStackTrace();  
    14.             }  
    15.         }  
    16.     }  

    三、一些思考

    1)单例模式是为了保证一个类只有一个实例,整个系统只能有自己创建的一个实例。应用在数据库连接或单个队列处理等问题。

    2)单例模式构造,懒汉模式以时间换空间(用的时候才生产实例,每次都判断);懒汉模式以空间换时间,直接创建实例,以后不用判断直接用。

    3)懒汉模式线程安全问题,获取实例的方法要加上synchronized进行同步。

    4)java的反射技术不要用于实例创建,反射主要用于Spring的IOC, Hibernate和白盒测试。

    ---------------------------------------------------------我是枚举分割线-------------------------------------------------------

    可以用枚举防止反射破坏单例模型(PS:JAVA 枚举是线程安全的)

    From:http://blog.csdn.net/tounaobun/article/details/8514989

    JDK1.5及以后,增加了实现Singleton的第三种方法。只需编写一个包含单个元素的枚举类型。

        

    Java代码 
    1. public enum Adam {  
    2.   
    3.     INSTANCE;   //只有一个元素  
    4.       
    5.     public void leaveTheBuilding() {;;;}   
    6. }  

        使用这种方法的好处是可以防止多次实例化,无偿提供了序列化机制,即使是面对复杂的序列化或者反射公鸡。

        当你试图通过反射调用枚举类型的构造器时(默认构造器为private),如果调用了setAccessible(true)方法,将会抛出IllegalArgumentException:

    Java代码 
    1. Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects  
    2.     at java.lang.reflect.Constructor.newInstance(Unknown Source)  
    3.     at org.reflect.Test.main(Test.java:23)  

        对于前两种单例模式,为了使Singleton能够序列化,除了实现标记接口Serializable外,还需增加类似下面的方法,防止反序列化时生成“假冒”的单例类:

    Java代码 
    1. private Object readResolve() {  
    2.     return INSTANCE;  
    3. }  

         而对于枚举类型,完全不用多此一举。因为枚举类型已经提供了该机制。

  • 相关阅读:
    简单工厂、工厂方法、抽象工厂
    c#之反射总结
    设计模式Builder(建造者)模式
    c#之委托和事件的区别
    c#之委托总结
    javascript之流程控制 和函数的容易忽略点
    javascript之六种数据类型以及特殊注意点
    Sublime Text 快捷键
    链接中获取文件名
    js 获取当天23点59分59秒 时间戳 (最简单的方法)
  • 原文地址:https://www.cnblogs.com/luckygxf/p/4737554.html
Copyright © 2011-2022 走看看