java 单例模式实现方式

一、单例模式优点、注意事项及应用场景

 1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
 2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new。
 3. 单例模式 使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(比如:重量级对象),或者又经常用到的对象、 工具类对象、频繁访问数据库或文件的对象(比如 数据源、session 工厂等)。

二、单例模式实现的几种方式

1. 饿汉式

 代码如下所示:

public class Singleton {
	//1.定义私有化静态的本类单例对象实例。
	private static Singleton instance = null;
 
	/**
	  * 2.构造方法私有化,防止外部类new对象。
	  */
	private Singleton() {
 
	}
 
    //3.提供一个公有的静态方法,返回单例实例对象。
 
	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
 
}

 调用顺序时序图如下所示:
在这里插入图片描述
 单例模式的懒汉式体现了缓存的思想,延时加载就是一开始不要加载资源或者数据,一直等,等到马上就要使用这个资源或者数据了,躲不过去了才去加载。懒汉式是典型的时间换空间。

 但是不加同步锁的懒汉式是线程不安全的,如下示例:
在这里插入图片描述
 如何实现线程安全的懒汉式(即双重检查加锁),代码如下所示:

public class Singleton {
    //1.定义私有化静态的本类单例对象实例。
	private volatile static Singleton instance = null;
 
	// 2.构造方法私有化,防止外部类new对象。
	private Singleton() {
 
	}
    //3.提供一个公共的静态方法,返回单例实例对象。
	public static Singleton getInstance() {
		if (instance == null) {//3.1 第一重检查
			synchronized (Singleton.class) {//3.2 加同步锁
				if (instance == null) {//3.3 第二重检查
					instance = new Singleton();
				}
			}
 
		}
		return instance;
	}
 
}

2. 懒汉式

 代码如下所示:

public class Singleton {
    //1.创建一个私有化静态对象。
	private static Singleton instance = new Singleton();
 
	//2.私有化构造方法,防止外部类new对象。
	private Singleton()  
	}
    //3.提供一个公共的静态方法,返回单例实例对象。
	public static Singleton getInstance() {
		return instance;
	}
 
}

 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要判断了,节省了运行时间。

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

 代码如下所示:

public class Singleton {
    //1.私有化静态内部类。
	private static class SingletonHoler {
		/**
		 * 静态初始化器,由JVM来保证线程安全
		 */
		private static Singleton instance = new Singleton();//创建单例实例对象
	}
    //2.私有化单例类构造方法,防止外部类new对象。
	private Singleton() {
	}
    //3.提供一个公共的静态方法,返回单例实例对象。
	public static Singleton getInstance() {
		return SingletonHoler.instance;
	}
 
}

 当getInstance方法第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

4.使用枚举来实现单例

 代码如下所示:

class Singleton{
}

public enum SingletonEnum {
    INSTANCE;
    private Singleton instance;
    SingletonEnum() {
        instance = new Singleton();
    }
    public Singleton getInstance() {
        return instance;
    }
}

 上面的类Singleton是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。获取资源的方式很简单,只要 SingletonEnum.INSTANCE.getInstance() 即可获得所要实例。下面我们来看看单例是如何被保证的:首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法,同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。可以看到,枚举实现单例还是比较简单的。

5.单例模式在源码中的应用

在这里插入图片描述

三、破坏单例模式的三种方式及解决方法

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

 代码如下所示:

/**
 * 序列化对单例的破坏
 * @author Administrator
 *
 */
public class SingletonTest09 {
	
	public static void main(String[] args) throws Exception{
		
		System.out.println("-----------序列化----------------------");
	    Singleton originSingleton = Singleton.getInstance();
	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
	    ObjectOutputStream oos = new ObjectOutputStream(bos);
	    oos.writeObject(Singleton.getInstance());
	    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
	    ObjectInputStream ois = new ObjectInputStream(bis);
	    Singleton serializeSingleton = (Singleton) ois.readObject();
	    System.out.println(originSingleton == serializeSingleton);//false
	    
	    System.out.println("-----------反射----------------------");
	    //通过反射获取
	    Constructor<Singleton> cons = Singleton.class.getDeclaredConstructor();
	    cons.setAccessible(true);
	    Singleton reflextSingleton = cons.newInstance();
	    System.out.println(reflextSingleton == originSingleton);//false
	    
	    System.out.println("---------------------------克隆----------------------");
	    
	    Singleton cloneSingleton = (Singleton) originSingleton.clone();
	    System.out.println(cloneSingleton == originSingleton);//false
	    
	}
	
	private static class Singleton  implements Serializable,Cloneable{
		//1.定义私有化静态的本类单例对象实例。
		private static  volatile  Singleton instance;
		
		//2.私有化构造方法,防止外部类new对象。
		private Singleton() {}
		
		//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 super.clone();
		 }
		 
	}
	
}
 

解决方案如下:

1、防止反射

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

2、防止克隆破坏

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

3、防止序列化破坏

 添加readResolve(),返回Object对象

代码如下所示:

**
 * 序列化对单例的破坏,解决方案
 * @author Administrator
 *
 */
public class SingletonTest10 {
	
	public static void main(String[] args) throws Exception{
		
		System.out.println("-----------序列化----------------------");
	    Singleton originSingleton = Singleton.getInstance();
	    ByteArrayOutputStream bos = new ByteArrayOutputStream();
	    ObjectOutputStream oos = new ObjectOutputStream(bos);
	    oos.writeObject(Singleton.getInstance());
	    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
	    ObjectInputStream ois = new ObjectInputStream(bis);
	    Singleton serializeSingleton = (Singleton) ois.readObject();
	    System.out.println(originSingleton == serializeSingleton);//false
	    
	    System.out.println("-----------反射----------------------");
	    //通过反射获取
	    Constructor<Singleton> cons = Singleton.class.getDeclaredConstructor();
	    cons.setAccessible(true);
	    Singleton reflextSingleton = cons.newInstance();
	    System.out.println(reflextSingleton == originSingleton);//false
	    
	    System.out.println("---------------------------克隆----------------------");
	    
	    Singleton cloneSingleton = (Singleton) originSingleton.clone();
	    System.out.println(cloneSingleton == originSingleton);//false
	    
	}
	
	private static class Singleton  implements Serializable,Cloneable{
		
		private static volatile boolean isCreate = false;//全局变量,默认是第一次创建
		
		//1.定义私有化静态的本类单例对象实例。
		private static  volatile  Singleton instance;
		
		//2.私有化构造方法,防止外部类new对象。
		private Singleton() {
			if(isCreate) {
				throw new RuntimeException("已然被实例化一次,不能在实例化");
			}
			isCreate = true;
		}
		
		//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;
	    }
		 
	}
	
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值