总结了两种方法,如果还有别的方法的话,欢迎博友补充
一、反射机制的引入来破坏单例模式
下面是一个单例类,我们通过反射机制生成该类的一个实例
通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消 Java 语言访问检查,
使得私有的构造函数能够被访问,这样使得单例模式失效。
下面是一个单例类,我们通过反射机制生成该类的一个实例
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的单例类,看序列化如何生成单例类的一个实例
序列化可以实现从字节码生成对象,因此序列化也有可能破坏单例模式
下面给出一个实现了序列化接口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方法来返回我们指定好的对象了,单例规则也就得到了保证