防止单例模式被破坏

总结了两种方法,如果还有别的方法的话,欢迎博友补充
一、反射机制的引入来破坏单例模式
下面是一个单例类,我们通过反射机制生成该类的一个实例
public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    public Singleton getInstance() {
        return INSTANCE;
    }
    public static void main(String[] args) throws Exception {
        // 反射机制破坏单例模式
        Class clazz = Singleton.class;
  //获得构造器
        Constructor c = clazz.getDeclaredConstructor();
        // 反射机制使得private方法可以被访问
        c.setAccessible(true);
        // 判断反射生成的对象与单例对象是否相等
        System.out.println(Singleton.INSTANCE == c.newInstance());
    }
}


通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消 Java 语言访问检查,
使得私有的构造函数能够被访问,这样使得单例模式失效。
二、序列化机制来破坏单例模式
序列化可以实现从字节码生成对象,因此序列化也有可能破坏单例模式
下面给出一个实现了序列化接口Serializable的单例类,看序列化如何生成单例类的一个实例
public class Singleton implements Serializable {
    public static final Singleton INSTANCE = new Singleton();
    private Singleton() {
    }
    public static void main(String[] args) throws Exception {
        // 支持java.io.Serializable的对象都可以写入流中
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(Singleton.INSTANCE);
        // 根据字节流生成对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Singleton newSingleton = (Singleton) ois.readObject();
        System.out.println(newSingleton == Singleton.INSTANCE);
    }
}


单例模式唯一实例为什么必须为静态?
那么问题的关键来了,程序调用类中方法只有两种方式,①创建类的一个对象,用该对象去调用类中方法;②使用类名直接调用类中方法,格式“类名.方法名()”;
上面说了,构造函数私有化后第一种情况就不能用,只能使用第二种方法。
而使用类名直接调用类中方法,类中方法必须是静态的,而静态方法不能访问非静态成员变量,因此类自定义的实例变量也必须是静态的。
这就是单例模式唯一实例必须设置为静态的原因。

防止反射破坏单利模式
 
public class ElvisModified  
{  
    private static boolean flag = false;  
 
    private ElvisModified(){  
        synchronized(ElvisModified.class)  
        {  
            if(flag == false)  
            {  
                flag = !flag;  
            }  
            else 
            {  
                throw new RuntimeException("单例模式被侵犯!");  
            }  
        }  
    }  


防止序列化破坏单利模式
private Object readResolve(){
return instance;
}

一般来说,一个类实现了 Serializable接口,我们就可以把它往内存里写再从内存里读出而"组装"成一个跟原来一模一样的对象
当JVM从内存中反序列化地"组装"一个新对象时,就会自动调用这个 readResolve方法来返回我们指定好的对象了,单例规则也就得到了保证

猜你喜欢

转载自blog.csdn.net/MonkeyITBoy/article/details/80018855
今日推荐