设计模式-单例模式
2016-03-12 20:53
337 查看
1.单例模式介绍
单例模式是应用最广的模式之一。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多系统只需要拥有一个全局的对象,遮掩更有利于我们协调整体的统一行为。2.单例模式的定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。3.单例模式的使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对只应该有且只有一个。例如,创建 一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式4.单例模式的分类
饿汉模式和懒汉模式5.单例模式的UML类图
6.实现单例模式的关键点
1.构造函数对外不开放,一般为private2.通过一个静态方法或者枚举返回单例对象
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设计模式之单例模式