原文网址:Java设计模式系列--单例模式(破坏单例的方法)_IT利刃出鞘的博客-CSDN博客
简介
说明
本文介绍Java破坏单例模式的方法。
单例模式(Singleton Pattern)是指确保一个类只有一个实例,并提供一个全局访问点。破坏单例模式的话,就是说可以创建出两个或两个以上的实例。本文通过静态内部类方式进行说明。
破坏单例的方法
破坏单列模式的方式
- 反射
- 序列和反序列化
- 使用不同的类加载器
上边1, 2都可以用枚举来解决,具体见:此文。
反射
破坏单例模式
单例类
package org.example.a;public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton() {}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
测试类
package org.example.a;import java.lang.reflect.Constructor;public class Demo {public static void main(String[] args) {Class<?> singletonClass = Singleton.class;try {Constructor c = singletonClass.getDeclaredConstructor(null);c.setAccessible(true);Object singleton1 = c.newInstance();Object singleton2 = c.newInstance();System.out.println(singleton1 == singleton2);} catch (Throwable e) {e.printStackTrace();}}
}
执行结果(创建出了两个不同的实例)
false
防止反射破坏单例模式
在构造函数里防止反射。
单例类
package org.example.a;public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton() {if (SingletonHolder.INSTANCE != null) {throw new RuntimeException("不允许通过反射创建实例");}}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
测试类(通过反射获得实例)
package org.example.a;import java.lang.reflect.Constructor;public class Demo {public static void main(String[] args) {Class<?> singletonClass = Singleton.class;try {Constructor c = singletonClass.getDeclaredConstructor(null);c.setAccessible(true);Object singleton1 = c.newInstance();System.out.println("singleton1 instance create successfully");Object singleton2 = c.newInstance();System.out.println("singleton2 instance create successfully");System.out.println(singleton1 == singleton2);} catch (Throwable e) {e.printStackTrace();}}
}
执行结果
Caused by: java.lang.RuntimeException: 不允许通过反射创建实例
测试类(通过静态方法获得实例)
package org.example.a;public class Demo {public static void main(String[] args) {Singleton singleton = Singleton.getInstance();System.out.println("singleton create successfully");}
}
执行结果
singleton create successfully
防止反射破坏单例模式的原理
1.用getInstance
调用到new Singleton()时,此时INSTANCE是null,所以不会报错。
2.用反射
调用构造方法时,调用到Singleton.INSTANCE,然后会再new Singleton()赋值给Singleton.INSTANCE,此时,Singleton.INSTANCE不为null。
序列化与反序列化
破坏单例模式
单例类
package org.example.a;import java.io.Serializable;public class Singleton implements Serializable {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton() {}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
测试类
package org.example.a;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class Demo {public static void main(String[] args) {Singleton s1 = null;Singleton s2 = Singleton.getInstance();FileOutputStream fos = null;try {//序列化到文件中fos = new FileOutputStream("Singleton.obj");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(s2);oos.flush();oos.close();//从文件中反序列化为对象FileInputStream fis = new FileInputStream("Singleton.obj");ObjectInputStream ois = new ObjectInputStream(fis);s1 = (Singleton) ois.readObject();ois.close();//对比结果System.out.println(s1 == s2);} catch (Exception e) {e.printStackTrace();}}
}
执行结果(产生多个实例)
false
防止序列化/反序列化破坏单例模式
单例类
package org.example.a;import java.io.Serializable;public class Singleton implements Serializable {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton() {}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}private Object readResolve() {return SingletonHolder.INSTANCE;}
}
测试类
package org.example.a;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class Demo {public static void main(String[] args) {Singleton s1 = null;Singleton s2 = Singleton.getInstance();FileOutputStream fos = null;try {//序列化到文件中fos = new FileOutputStream("Singleton.obj");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(s2);oos.flush();oos.close();//从文件中反序列化为对象FileInputStream fis = new FileInputStream("Singleton.obj");ObjectInputStream ois = new ObjectInputStream(fis);s1 = (Singleton) ois.readObject();ois.close();//对比结果System.out.println(s1 == s2);} catch (Exception e) {e.printStackTrace();}}
}
执行结果(保护了单例模式)
true
防止序列化/反序列化破坏单例模式的原理
追踪序列化源码
java.io.ObjectInputStream#readObject
readObject //java.io.ObjectInputStreamreadObject0(false) //java.io.ObjectInputStream。case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared));readOrdinaryObject(unshared) //java.io.ObjectInputStreamObject obj;try {//判断是否有无参的构造函数,有的话就调用newInstance()实例化对象obj = desc.isInstantiable() ? desc.newInstance() : null;} catch (Exception ex) {throw (IOException) new InvalidClassException(desc.forClass().getName(),"unable to create instance").initCause(ex);}//其他代码if (obj != null &&handles.lookupException(passHandle) == null &&desc.hasReadResolveMethod()){Object rep = desc.invokeReadResolve(obj);//其他代码}
关键是desc.hasReadResolveMethod() ,这段代码的意思是查看你的单例类里面有没有readResolve方法,有的话就利用反射的方式执行这个方法,具体是desc.invokeReadResolve(obj)这段代码,返回单例对象。这里其实是实例化了两次,只不过新创建的对象没有被返回而已。如果创建对象的动作发生频率增大,就意味着内存分配开销也就随之增大,这也算是一个缺点吧。
不同的类加载器
不同的类加载器破坏单例模式
单例类
package org.example.a;import java.io.Serializable;public class Singleton implements Serializable {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton() {}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
测试类
package org.example.a;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;public class Demo {public static void main(String[] args) throws Throwable{ClassLoader myLoader = new ClassLoader() {@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {try {String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";InputStream is = getClass().getResourceAsStream(fileName);if (is == null) {return super.loadClass(name);}byte[] b = new byte[is.available()];is.read(b);return defineClass(name, b, 0, b.length);} catch (IOException e) {throw new ClassNotFoundException(name);}}};// 由系统应用程序类加载器加载Singleton instance1 = Singleton.getInstance();// 自定义的类加载器加载Class<?> clazz = myLoader.loadClass("org.example.a.Singleton");Constructor<?> constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true);Object instance2 = constructor.newInstance();System.out.println(instance2.getClass());System.out.println(instance2 == instance1);}
}
运行结果
class org.example.a.Singleton
false
防止不同的类加载器破坏单例模式
没有本质上解决方法。只能说,通过代码来保证使用的是同一个加载器:
private static Class<?> getClass(String classname) throws ClassNotFoundException {ClassLoader loader = Thread.currentThread().getContextClassLoader();if (loader == null) {loader = Singleton.class.getClassLoader();}return (loader.loadClass(classname));
}