设计模式-单例模式

1.单例模式介绍

单例模式是应用最广的模式之一。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多系统只需要拥有一个全局的对象,遮掩更有利于我们协调整体的统一行为。

2.单例模式的定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

3.单例模式的使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对只应该有且只有一个。例如,创建 一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式

4.单例模式的分类

饿汉模式和懒汉模式

5.单例模式的UML类图


6.实现单例模式的关键点

1.构造函数对外不开放,一般为private
2.通过一个静态方法或者枚举返回单例对象
3.确保单例类的对象有且只有一个,尤其是在多线程情况下
4.确保单例对象在反序列化时不会被重新构建

7.单例模式的几种写法

7.1 饿汉模式

饿汉模式是类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的

	// 普通人群
	public class Person {
		String name;
		int age;

		public Person() {
		}

		public void work() {

		}
	}
	
	// 教师
	public class Teacher extends Person {
		public void work() {

		}
	}
	
	// 校长
	public class Principal extends Person {
		private static final Principal mPrincipal = new Principal();

		// 构造函数私有
		private Principal() {

		}

		// 公有的静态函数,对外暴露获取单例对象的接口
		public static Principal getInstance() {
			return mPrincipal;
		}

	}
	
	public class SingletonDemo {
		public static void main(String[] args) {
			// 教师对象通过New获取
			Teacher tea_0 = new Teacher();
			Teacher tea_1 = new Teacher();

			// 校长对象通过getInstance方法获取
			Principal pri_0 = Principal.getInstance();
			Principal pri_1 = Principal.getInstance();

			// 普通人群对象通过New获取
			Person per_0 = new Person();
			Person per_1 = new Person();

			System.out.printf("tea_0: " + tea_0.toString() + "\n");
			System.out.printf("tea_1: " + tea_1.toString() + "\n");
			System.out.printf("pri_0: " + pri_0.toString() + "\n");
			System.out.printf("pri_1: " + pri_1.toString() + "\n");
			System.out.printf("per_0: " + per_0.toString() + "\n");
			System.out.printf("per_1: " + per_1.toString() + "\n");
		}

	}

7.2 懒汉模式

懒汉模式是声明一个静态对象,并且用户在第一次调用getInstance时进行初始化。
优点:单例只有在使用时才会被实例化,在一定程度上节省了资源
缺点:第一次加载时需要及时进行实例化,反应稍慢,最大的问题在于每次调用getInstance都进行同步,造成不必要的同步开销
 
	Public class Principal extends Person {
		/** 懒汉模式 */
		private static Principal mPrincipal = null;

		// 构造函数私有
		private Principal() {

		}

		public static Principal getInstance() {
			if (null == mPrincipal) {
				mPrincipal = new Principal();
			}

			return mPrincipal;
		}
	}

7.3 DCL(Double Check Lock)实现单例

在getInstance方法中对mPrincipal进行两次判断,第一次判断主要是为了避免不必要的同步,第二次判断主要是为了在null的情况下创建实例并保证线程安全
优点:既能够在需要时才进行实例初始化,同时保证了线程安全,且单例对象实例化后,调用getInstance不进行同步锁
缺点:第一次加载时反应速度慢,由于JVM内存模型,偶尔会出现实例化失败

	public class Principal extends Person {
		/** DCL模式 */
		private static Principal mPrincipal = null;

		// 构造函数私有
		private Principal() {

		}

		public static Principal getInstance() {
			if (null == mPrincipal) {
				synchronized (Principal.class) {
					if (null == mPrincipal) {
						mPrincipal = new Principal();
					}
				}
			}
			return mPrincipal;
		}
	}

7.4 静态内部类单例模式

当第一次加载Principal时并不会初始化mPrincipal,只有在第一次调用Principal的getInstance才会实例化mPrincipal。因此在第一次调用getIntance方法时加载PrincipalHolder,这种方式不仅能够保证线程安全也能够保证实例化对象的唯一性。
	public class Principal extends Person {
		 /** 静态内部类实现单例模式 */
		// 构造函数私有
		private Principal() {

		}

		public static Principal getInstance() {
			return PrincipalHolder.mPrincipal;
		}

		private static class PrincipalHolder {
			private static Principal mPrincipal = new Principal();
		}
	}

7.5枚举单例

默认枚举创建单例是线程安全的,并且在任何情况下,它都是一个单例。
	public enum Principal {  
  
		INSTANCE;  
	  
		private Principal(){  
	  
		}  
	  
		public void work(){  
		}  
	  
	}  
	使用
	Principal.INSTANCE.work();

7.6 登记模式

在程序初始化时,将多个单例类型注入到一个统一的管理类中,在使用时,根据key获取对应类型的对象,这种方式除了方便我们管理多种类型的单例,
并且在使用时通过统一的接口进行获取操作,实现了对用户的隐藏。
	public class SingletonManager {
		private static Map<String, Object> objMap = new HashMap<>();

		private SingletonManager() {

		}

		public static void registerSingleton(String key, Object instance) {
			if (!objMap.containsKey(key)) {
				objMap.put(key, instance);
			}
		}

		public static Object getSingleton(String key) {
			return objMap.get(key);
		}
	}</span>


参考资料:

1.Android源码设计模式时间

2.JAVA设计模式之单例模式


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值