单例模式(四种)与其破坏方式(三种)

单例模式

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

单例模式的类型

单例模式有两种类型:

  • 懒汉式:在真正需要使用对象时才去创建该单例类对象
  • 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用

饿汉式:

  • 线程安全
  • 不能实现延迟加载(浪费空间)
public class Singleton {
    
    

    private static Singleton instance = new Singleton();

    private Singleton() {
    
    
    }

    public static Singleton getInstance() {
    
    
        return instance;
    }
} 

不管程序是否需要这个对象的实例,总是在类加载的时候就先创建好实例,理解起来就像不管一个人想不想吃东西都把吃的先买好,如同饿怕了一样。

懒汉式:

最初懒汉式0

  • 延迟加载
  • 非线程安全
public class Singleton {
    
    

    private static Singleton instance = null;

    private Singleton() {
    
    
    }

    public static Singleton getInstance() {
    
    
        if (null == instance) {
    
    
            instance = new Singleton();
        }
        return instance;
    }
}   

如果一个对象使用频率不高,占用内存还特别大,明显就不合适用饿汉式了,这时就需要一种懒加载的思想,当程序需要这个实例的时候才去创建对象,就如同一个人懒的饿到不行了才去吃东西。

懒汉式1

  • 延迟加载
  • 线程安全 - 但每次获取都需要加锁,性能低下
public class Singleton {
    
    
	private static Singleton instance = null;
	private Singleton() {
    
    
 
	}
	public static  synchronized Singleton getInstance() {
    
    
		if (instance == null)
		  instance = new Singleton()
		}
		return instance;
	}

懒汉式2(双重检查加锁):

  • 延迟加载
  • 线程安全 - 效率相比1提升,创建后不必每次加锁
public class Singleton {
    
    
	private volatile static Singleton instance = null;
	private Singleton() {
    
    
 
	}
 
	public static Singleton getInstance() {
    
    
		if (instance == null) {
    
    
			synchronized (Singleton.class) {
    
    
				if (instance == null) {
    
    
					instance = new Singleton();
				}
			}
 
		}
		return instance;
	}
 
}

使用静态内部类实现单例模式

  • 延迟加载
  • 线程安全(利用类加载机制,保证线程安全) : 双亲委派机制保证一个类只加载一次,从而保证线程安全
public class Singleton {
    
    
	private static class SingletonHoler {
    
    
		/**
		 * 静态初始化器,由JVM来保证线程安全
		 */
		private static Singleton instance = new Singleton();
	}
 
	private Singleton() {
    
    
	}
 
	public static Singleton getInstance() {
    
    
		return SingletonHoler.instance;
	}
 
}

使用枚举来实现单例

public enum Singleton {
    
    
	uniqueInstance;// 定义一个枚举的元素,它 就代表了Singleton的一个实例
 
	public void singletonOperation() {
    
    
		// 功能处理
		System.err.println("功能处理");
	}
 
}

破坏单例模式的三种方式

  1. 反射
  2. 序列化
  3. 克隆

解决方案如下

1、防止反射

定义一个全局变量,当第二次创建的时候抛出异常

		private static volatile boolean isCreate = false;//默认是第一次创建
		/**
		 * 1.构造方法私有化,外部不能new
		 */
		private Singleton() {
    
    
			if(isCreate) {
    
    
				throw new RuntimeException("已然被实例化一次,不能在实例化");
			}
			isCreate = true;
		}

2、防止克隆破坏

重写clone(),直接返回单例对象

		@Override
		 protected Object clone() throws CloneNotSupportedException {
    
    
		     return instance;
		 }

3、防止序列化破坏

从序列化中恢复一个单例对象会破坏单例模式,解决方法是添加readResolve()

原理:

  1. 反序列化时,首先获取序列化的类 : desc( 可理解为单例类的class类,但它和JVM加载到内存中的单例class类有不同)因为如果desc就是我们的单例class类,那是不允许再实例化的。而desc类却可以实例化。
  2. 判断对象是否能实例化。可以则进行实例化,至此单例类进行了第一次实例化,对象名为obj
  3. 第一次实例化完成后,通过反射寻找该单例类中的readResolve()方法,没有则直接返回obj对象。(这就是直接反序列化破坏单例模式的原因)
  4. 有定义readResolve()方法,desc通过invokeReadResolve(Object obj)方法调用readResolve()方法获取单例对象instance,将他赋值给rep,如果单例对象之前已经被实例化过,那么rep就会指向之前实例化的单例对象。如果我们之前没有实例化单例对象,则rep会指向null。
  5. rep与obj进行比较,由于obj是反射获取的对象,当然与rep不等,于是将rep的值instance赋值给obj,将obj返回,返回对象instance也就保证了单例。

简而言之就是,当我们通过反序列化readObject()方法获取对象时会去寻找readResolve()方法,如果该方法不存在则直接返回新对象,如果该方法存在则按该方法的内容返回对象,以确保如果我们之前实例化了单例对象,就返回该对象如果我们之前没有实例化单例对象,则会返回null
参考文章

		/**
		 * 防止序列化破环
		 * @return
		 */
		private Object readResolve() {
    
    
	        return instance;
	    }

最终版

private static class Singleton  implements Serializable,Cloneable{
    
    
		
		private static volatile boolean isCreate = false;//默认是第一次创建
		/**
		 * 1.构造方法私有化,外部不能new
		 */
		private Singleton() {
    
    
			if(isCreate) {
    
    
				throw new RuntimeException("已然被实例化一次,不能在实例化");
			}
			isCreate = true;
		}
		
		
		//2.本类内部创建对象实例
		private static  volatile  Singleton instance;
		
		
		//3.提供一个公有的静态方法,返回实例对象
		public static  Singleton getInstance() {
    
    
			if(instance == null) {
    
    
				synchronized (Singleton.class) {
    
    
					if(instance == null) {
    
    
						instance = new Singleton();
					}
				}
			}
			return instance;
		}
		
		@Override
		 protected Object clone() throws CloneNotSupportedException {
    
    
		     return instance;
		 }
		
		/**
		 * 防止序列化破环
		 * @return
		 */
		private Object readResolve() {
    
    
	        return instance;
	    }
		 
	}

猜你喜欢

转载自blog.csdn.net/qq_39304851/article/details/115679982