「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。 在我们使用单例模式的过程中,可以通过使用反射,序列化,克隆来破坏单例模式,那么我们应该使用什么方式来防止单例模式被破坏呢?
一、如何防止反射破坏单例
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
PreventReflectSingle singleA = PreventReflectSingle.getInstance();
Class<?> clazz = Class.forName("com.cn.model.pr.PreventReflectSingle");
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
PreventReflectSingle singleB = (PreventReflectSingle) declaredConstructor.newInstance();
System.out.println(singleA);
System.out.println(singleB);
System.out.println(singleA==singleB);
}
可以看到在构造方法中进行了判断,如果preventReflectSingle已经存在,直接抛出异常。阻止我们通过反射绕过私有构造方法创建对象。一旦我们通过反射,修改访问权限,然后创建对象,就会直接抛出异常(注意此处存在线程安全问题,可能另一个线程在调用getInstance方法初始化对象的时候,一个线程通过反射创建对象,导致出现两个实例)。
二、如何防止克隆破坏单例
public static void main(String[] args) {
PreventCloneSingle singleA = PreventCloneSingle.getInstance();
PreventCloneSingle singleB = (PreventCloneSingle) singleA.clone();
System.out.println(singleA);
System.out.println(singleB);
System.out.println(singleA==singleB);
}
此处怎么防止克隆破坏单例呢,我们知道克隆对象就是调用clone方法,那么我们只需要让方法直接返回已经创建的对象,而不是复制一个对象,不就可以有效防止通过克隆创建对象,从而打破单例的情况了吗,从下图的结果中我们可以看到,我们最后得到的是同一个对象。
三、如何防止序列化破坏单例
public static void main(String[] args) throws IOException, ClassNotFoundException {
PreventSerializeSingle singleA = PreventSerializeSingle.getInstance();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("temp"));
objectOutputStream.writeObject(singleA);
File file = new File("temp");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
PreventSerializeSingle singleB = (PreventSerializeSingle) objectInputStream.readObject();
System.out.println(singleA);
System.out.println(singleB);
System.out.println(singleA==singleB);
}
把对象序列化回来时,调用readObject方法,而在readObject方法的实现中,会判断这个对象自身是否有readResolve方法,如果没有此方法时,会直接返回之前新创建的对象,如果有这个方法,会按照这个方法执行的结果返回对象。此处我们直接让这个方法返回我们我们定义的静态变量,如果为空,最后的结果也是null,如果已经初始化,反序列化回来的结果与之前获得的对象是一致的,如下图所示,序列化前后是同一对象,我们成功的阻止了单例被破坏。