破坏单例模式的几种方式
所谓单例模式就是,某一个类只能有一个实例,实现的核心就是将类的[构造函数]私有化,只能由该类的静态方法在其内部创建对象,外面不能通过new 来调用构造函数来创建对象了。
只有一个实例的类才遵循单例模式,要想研究如何去破坏单例模式,我们首先来看看创建实例的方式有哪些,也就是说, 对象的创建方式有哪几种?
就本人目前想到的有四种:new 、克隆、序列化、反射。
反射破坏
虽然说在设计单例模式的时候将构造方法私有化了,但是,有一个非常厉害的哥们儿–反射,利用反射机制可以获取类的构造方法,再加一行 setAccessible(true)设置构造方法的可见性,就可以调用私有的构造函数,创建对象进而破坏单例模式了。这是一种情况,通过反射破坏单例。
怎么解决呢?可以在构造方法中设置一个布尔标志, 当第二次调用构造方法时抛出异常。如下所示
/**
*
*懒汉式
*/
public class Singleton {
private static volatile Singleton instance;
private static boolean flag=true;
private Singleton(){
if(flag){
flag=false;
}
else {
throw new RuntimeException("单例模式遇到攻击,第二个对象未创建成功");
}
}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
序列化破坏
当我们需要将某个对象进行保存或者远程传输时,可以将一个类实现序列化接口,一是可以实现数据的持久化;二是可以对象数据的远程传输。 如果过该类实现了序列化接口(implements Serializable),那么就会在序列化后反序列化时再创一个对象, (序列化前的对象和反序列化后得到的对象内存地址不同) ,破坏了单例模式 。因为序列化时会通过反射调用无参数的构造方法创建一个新的对象。
解决办法是在反序列化时,指定反序化的对象实例是我们创建出来的单例对象。即只要在Singleton类中定义readResolve
就可以解决该问题:
public class Singleton implements Serializable {
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized (Singleton.class) {
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
private Object readResolve(){
return instance;
}
}
(我们的类中有readResolve方法时在反序列化创建对象时就会调用我们声明的readResolve方法去创建对象,若果我们的类中没有声明该方法那么在反序列化就利用反射机制去创建一个新的对象,进而破坏单例)
克隆破坏单例
由克隆我们可以想到原型模式,原型模式就是通过clone方法实现对象的创建的,clone方式是Object方法,每个对象都有,那么使用一个单例模式类的对象,调用clone方法,再创建一个新的对象了,那岂不是上面说的单例模式失效了。
当然答案是否定,某一个对象直接调用clone方法,会抛出异常,即并不能成功克隆一个对象。调用该类对象的该方法时,该类对象必须实现一个Cloneable 接口。这也就是原型模式的实现方式。还有即如果该类实现了cloneable接口,尽管构造函数是私有的,他也可以创建一个对象。即clone方法是不会调用构造函数的,他是直接从内存中copy内存区域的。
解决办法:单例模式的类不实现cloneable接口,不实现该接口也就不能进行克隆,自然无法破坏单例。