书山有路勤为径,学海无涯苦作舟
今天这一篇,我们继续来讨论关于单例设计模式中的一些问题和细节
1.反射攻击,破坏单例模式
在单例设计模式中的所有实现中,都会存在反射攻击的安全隐患,接下来我们来看一段代码。研究一下为什么会存在反射攻击的隐患呢?
//老套路。来一段main方法
public class InnerClassSingletonTest{
public static void main(String[] args) throws Exception{
//接下来我们通过反射的方式来创建一个单例模式的实例
//获取该类的构造方法
Constructor<InnerClassSingleton> declaredConstructor=InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
//通过构造方法实例化
InnerClassSingleton innerSingleton = declaredConstructor.newInstance();
//通过正常调用实例化
InnerClassSIngleton instance1= InnerClassSIngleton.getInstance();
System.out.println(instance==insrance1);
//控制台打印结果为false,单例被破坏,产生了两个对象
}
}
//声明静态内部类创建方法的具体类
class InnerClassSingleton{
private static InnerHolder{
private static InnerClassSingleton instance=new InnerClassSingleton ();
}
private InnerClassSingleton(){}
public InnerClassSingletongetInstance(){
return InnerHolder.instance;
}
}
破坏原因:class反射通过暴力反射的形式,强行获取到我们的构造方法,从而创建了实例,破坏单实例模式
那么我们来思考一下能不能防止反射攻击呢?答案是肯定的,可以防止
接下来我们再来看一段代码
//私有的构造方法
private InnerClassSingleton(){
if(innerHolder.instance!=null){
throw new RuntimeException("单例模式下不允许创建多实例");
}
}
这样就可以防止反射攻击了
注意:只有饿汉模式和静态内部类的懒加载模式可以防止反射攻击,第一种懒加载是无法防止反射攻击的
备注:枚举不能被反射创建,同时也是线程安全的,基于JVM类加载机制(静态代码块)
2.序列化的能力,(数据的网络传递需要实现序列化)
只要让具体类实现一个接口即可(Serializable接口:起到一个标记的作用)
实现了序列化以后会破坏实力模式
解决方法
a.先动态生成序列化ID
b.在添加一个方法Object readResolve() throws ObjectStreamException{
return instance;
}