单例模式
单例模式就是指在某个系统中只存在一个实例。单例模式的要求:
- 私有构造方法
- 提供访问唯一对象的方法
单例模式分为两类:饿汉式、懒汉式
1、饿汉式
饿汉式:类加载就会导致该单实例对象被创建
缺点:如果该对象足够大的话,而一直没有使用就会造成内存的浪费
/** * 饿汉式一:使用静态变量 */ public class Singleton { //私有构造方法 private Singleton() {} //使用static修饰或者static代码块,在类加载后就创建了唯一的实例对象。 private static Singleton instance = new Singleton(); //对外提供静态方法获取该对象 public static Singleton getInstance() { return instance; } } /** * 饿汉式二:使用静态代码块 */ public class Singleton { //私有构造方法 private Singleton() {} //在成员位置创建该类的对象 private static Singleton instance; static { instance = new Singleton(); } //对外提供静态方法获取该对象 public static Singleton getInstance() { return instance; } }
2、懒汉式
懒汉式:类加载不会创建该单实例对象,而是首次使用该对象时才会创建
/** * 懒汉式(线程安全) */ public class Singleton { //私有构造方法 private Singleton() {} //volatile对静态变量的修饰能保证变量值在各线程访问时的同步性、唯一性 private volatile static Singleton instance; //对外提供静态方法获取该对象 public static Singleton getInstance() { //第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例 if(instance == null) { synchronized (Singleton.class) { //抢到锁之后再次判断是否为null(双检锁) if(instance == null) { instance = new Singleton(); } } } return instance; } }
3、破坏单例模式
方式一: 序列化
这种方式需要单例类实现序列化(Serializable)接口
//定义单例类,实现序列化接口 import java.io.Serializable; public class Singleton implements Serializable { private Singleton(){} private static volatile Singleton singleton; public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
破坏单例:
- 通过单例类获取实例对象,然后写入到文件中
- 读取该文件,生成实例类
//序列化的方式破坏单例 import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Test { /** * 将单例的实例对象写入文件 */ public static void writeObject2File() throws Exception { //获取Singleton类的对象 Singleton instance = Singleton.getInstance(); //创建对象输出流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/a.txt")); //将instance对象写出到文件中 oos.writeObject(instance); } /** * 读取文件,生成单例的实例对象 */ private static Singleton readObjectFromFile() throws Exception { //创建对象输入流对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt")); //第一个读取Singleton对象 Singleton instance = (Singleton) ois.readObject(); return instance; } public static void main(String[] args) throws Exception { //往文件中写对象 //writeObject2File(); //从文件中读取对象 Singleton s1 = readObjectFromFile(); Singleton s2 = readObjectFromFile(); //判断两个反序列化后的对象是否是同一个对象 System.out.println(s1 == s2);//false,生成了多个Singleton对象 } }
如何阻止破坏:
在Singleton类中添加`readResolve()`方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
//序列话破坏单例的解决方法:添加readResolve方法 import java.io.Serializable; public class Singleton implements Serializable { private Singleton(){} private static volatile Singleton singleton; public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } /** * 下面是为了解决序列化反序列化破解单例模式 */ private Object readResolve() { //返回单例对象,如果没有写这个方法就会返回一个新new的 return Singleton.getInstance(); } }
源码解析:ObjectInputStream
public final Object readObject() throws IOException, ClassNotFoundException{ ... // if nested read, passHandle contains handle of enclosing object int outerHandle = passHandle; try { Object obj = readObject0(false);//重点查看readObject0方法 ..... } private Object readObject0(boolean unshared) throws IOException { ... try { switch (tc) { ... case TC_OBJECT: return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法 ... } } finally { depth--; bin.setBlockDataMode(oldMode); } } private Object readOrdinaryObject(boolean unshared) throws IOException { ... //isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类, obj = desc.isInstantiable() ? desc.newInstance() : null; ... // 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) { // 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量 // 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。 Object rep = desc.invokeReadResolve(obj); ... } return obj; }

可以看出读取序列化文件时调用了readResolve方法
方式二:反射
利用反射来创建单例类的实例对象
import java.lang.reflect.Constructor; public class Test2 { public static void reflect() throws Exception { //获取Singleton类的字节码对象 Class clazz = Singleton.class; //获取Singleton类的私有无参构造方法对象 Constructor constructor = clazz.getDeclaredConstructor(); //取消访问检查 constructor.setAccessible(true); //创建Singleton类的对象s1 Singleton s1 = (Singleton) constructor.newInstance(); //创建Singleton类的对象s2 Singleton s2 = (Singleton) constructor.newInstance(); //判断通过反射创建的两个Singleton对象是否是同一个对象 System.out.println(s1 == s2);//false,说明生成了不同的实例对象 } public static void main(String[] args) throws Exception { reflect(); } }
如何阻止破坏 :
import java.io.Serializable; public class Singleton implements Serializable { //私有构造器 private Singleton(){ /** * 阻止反射破解单例模式: * 如果实例对象已经存在了就抛异常(构造方法返回不了实例对象,只能抛异常) */ if(singleton != null) { throw new RuntimeException(); } } private static volatile Singleton singleton; public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
4、完整的单例模式
import java.io.Serializable; public class Singleton implements Serializable { /** * 私有构造 */ private Singleton(){ /** * 阻止反射破解单例模式: * 如果实例对象已经存在了就抛异常(构造方法返回不了实例对象,只能抛异常) */ if(instance != null) { throw new RuntimeException(); } } private static volatile Singleton instance; /** * 获取单例对象 * @return */ public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } /** * 解决序列化反序列化破解单例模式 */ private Object readResolve() { return Singleton.getInstance(); } }
5、单例的应用
- Runtime类

浙公网安备 33010602011771号