Android设计模式——单例模式

来源:互联网 发布:小黄鸭淘客软件合法吗 编辑:程序博客网 时间:2024/06/08 02:57

单例模式

介绍

单例模式是应用最广的模式之一。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象。

定义

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

使用场景

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

使用单例模式的关键点:

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

    • 解释:通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式手动构造单例类的对象。单例类会暴露一个公有静态方法,客户端需要调用这个静态方法获取到单例类的唯一对象,在获取这个单例对象的过程中需要确保县城安全。即在多线程的环境下构造单例类的对象也是只有一个

实例:

模拟一个公司的员工情况,一个公司有多个员工Staff 有多个副总VP 但是只有一个CEO 无论在什么情况下,CEO总保持只有一个

  • Staff模型
/** * Describe:员工模型 * wx on 2016/12/29. */public class Staff {    public void work() {        System.out.println("staff is do something");    }}
  • VP模型
/** * Describe:VP模型 * wx on 2016/12/29. */public class VP extends Staff {    @Override    public void work() {        System.out.println("vp manager staff");    }}
  • CEO模型
/** * Describe:CEO模型 * wx on 2016/12/29. */public class CEO extends Staff {    private static CEO ceo = new CEO();    /**     * 私有构造方法     */    private CEO() {    }    /**     * 静态提供实例方法     */    public static CEO getInstance() {        return ceo;    }    @Override    public void work() {        System.out.println("ceo manager staff");    }}
  • Company模型
/** * Describe:公司模型 * wx on 2016/12/29. */public class Company {    private List<Staff> allStaffs = new ArrayList<Staff>();    public void addStaff(Staff staff) {        allStaffs.add(staff);    }    public void showAllStatffs() {        for (Staff allStaff : allStaffs) {            System.out.println("obj" + allStaff.toString());        }    }}
  • Test
/** * Describe: * wx on 2016/12/29. */public class Test {    public static void main(String[] args) {        Company cp = new Company();        // TODO: 创建CEO对象        CEO ceo1 = CEO.getInstance();        CEO ceo2 = CEO.getInstance();        // TODO: 入职        cp.addStaff(ceo1);        cp.addStaff(ceo2);        // TODO: 创建VP        Staff vp1 = new VP();        Staff vp2 = new VP();        // TODO: VP入职        cp.addStaff(vp1);        cp.addStaff(vp2);        Staff staff1 = new Staff();        Staff staff2 = new Staff();        Staff staff3 = new Staff();        // TODO: 员工入职        cp.addStaff(vp1);        cp.addStaff(vp2);        cp.addStaff(staff1);        cp.addStaff(staff2);        cp.addStaff(staff3);        // TODO: 查看所有员工        cp.showAllStatffs();    }}

运行结果:

objcom.individual.wx.singlemode.singleton.CEO@7d4991adobjcom.individual.wx.singlemode.singleton.CEO@7d4991adobjcom.individual.wx.singlemode.singleton.VP@28d93b30objcom.individual.wx.singlemode.singleton.VP@1b6d3586objcom.individual.wx.singlemode.singleton.VP@28d93b30objcom.individual.wx.singlemode.singleton.VP@1b6d3586objcom.individual.wx.singlemode.singleton.Staff@4554617cobjcom.individual.wx.singlemode.singleton.Staff@74a14482objcom.individual.wx.singlemode.singleton.Staff@1540e19d

单例的几种实现方式

懒汉式

  • 懒汉式是声明一个静态对象,并且在用户第一次调用getInstance时进行初始化,而饿汉式是在声明静态对象时就已经初始化。
  • 懒汉式实现:
/** * Describe:懒汉式单例模式 * wx on 2016/12/29. */public class SingleTon {    public static SingleTon instance;    private SingleTon() {    }    public static synchronized SingleTon getInstance() {        if (instance == null) {            instance = new SingleTon();        }        return instance;    }}
  • 存在问题:
    懒汉式单例模式实现中,即使instance已经被初始化,调用getInstance还是会进行同步,这样会消耗不必要的资源。

Double Check Lock (DCL) 实现单例

  • DCL实现单例模式的优点是既能在需要时才初始化单例,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁
/** * Describe:Double Check Lock 实现单例模式 * wx on 2016/12/29. */public class DCLSingleTon {    private static DCLSingleTon instance = null;    private DCLSingleTon() {    }    public static DCLSingleTon getInstance() {        // TODO: 第一次判空为了避免不必要的同步        if (instance == null) {            synchronized (SingleTon.class) {                // TODO: 第二次判空为了没有实例时创建实例对象                if (instance == null) {                    instance = new DCLSingleTon();                }            }        }        return instance;    }}

explain: 可见整个程序进行了两次非空判断,第一次的非空判断是为了避免不必要的同步,也就是说在instance为空的时候,也就是需要的时候才进行同步锁定。第二次非空判断,是为了实例并不存在的时候初始化。

DCL单例模式的有缺点
  • 缺点:第一次加载的时候反应会稍微慢,也会由于java内存模型的原因偶尔会失败
  • 优点:资源利用率高,在第一次执行getInstance()时单例对象才会被实例化,效率高
静态内部类实现单例模式
  • DLC虽然在一定程度上解决了资源消耗、多余同步、线程安全等问题,但是,在某些情况下还是会出现失效的问题。这个问题被称为双重检查锁定失效,在《Java并发编程实践》中给出优化方案:
/** * Describe:静态内部类实现单例模式 * wx on 2016/12/29. */public class InnerSingleTon {    private InnerSingleTon(){}    public static InnerSingleTon getInstance(){        return InnerSingleTonHolder.sInstance;    }    private static class InnerSingleTonHolder {        public static InnerSingleTon sInstance = new InnerSingleTon();    }}
  • 第一次加载InnerSingle类时并不会初始化sInstance,只有在第一次调用getInstance()的时候才会导致sInstance被初始化。

反序列化的问题

通过序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效地获得一个实例,即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于用该类的构造函数,反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的、被实例化方法readResolve(),这个方法可以让开发人员控制对象的反序列化。例如要杜绝单例对象在被反序列化时重新生成对象。那么必须加入如下方法:

private Object readResolve() throws ObjectStreamException{    return sInstance;}
枚举单例

使用枚举单例是好处是,写法简单,默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。枚举在反序列化时也不会重新生成新的实例

/** * Describe:枚举单例 * wx on 2016/12/29. */public enum  EnumSingleTon {    INSTANCE;    public void doSomeThing(){        System.out.println("enm do sth.");    }}
使用容器实现单例
/** * Describe:容器单例 * wx on 2016/12/29. */public class SingleTonManager {    private static Map<String, Object> objMap = new HashMap<String, Object>();    private SingleTonManager() {    }    public static void regiserService(String key, Object instance) {        if (!objMap.containsKey(key)) {            objMap.put(key, instance);        }    }    public static Object getService(String key) {        return objMap.get(key);    }}
  • 在程序的开始,将许多单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式可以让我们管理多种类型的单例。并且在使用时可以通过统一的接口进行获取操作

Android源码中的单例模式

  • 在Android系统,我们会经常会通过Context获取系统级别的服务,如WindowManagerService、ActivityManagerService等。更常用的是一个LayoutInflater的类。这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取。
  • 在Android系统中,还有类似于LayoutInflate也是在运行时先注册到系统服务器管理中,然后在使用时直接根据名字来进行使用的单例模式
  • 由于水平比较菜,所以还不不贴源码了,但是在Android源码中,还是有很多单例的设计模式的使用地方的,一定要去看看啊

总结

虽然在客户端中没有太多的高并发的情况,因此单例模式的使用与否并不会有太大的影响。在使用单例模式时一定要注意由于系统中只存在一个对象,那么对于这个对象的赋值和操作会影响到整个系统的使用,在这点还是应该注意的。一般对于资源消耗大的类,方便程序的性能提升 还是比较推荐使用单例模式来进行对象的管理的。

优点:

1.由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建和销毁时,单例模式就非常明显了
2. 由于单例模式只生成一个实例,所以减少了系统性能的开销。当一个对象的产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个对象,然后永久驻留内存的方式来解决
3. 单例模式可以避免对资源的多重占用。
4. 单例模式可以在系统设置全局的访问点,优化和共享资源访问。

缺点

1.单例模式一般没有接口,拓展比较困难
2.单例对象如果持有Context,那么很容易发生内存泄漏,需要注意传递给单例对象的Context最好是Application Context.

0 0
原创粉丝点击