Android面试问到单例咋整?

前言

单例模式是java设计模式之一。这种模式涉及到一个单一的类,该类负责建立本身的对象,并确保是单一的对象。这个类提供直接访问其单一对象的方式,且不须要实例化该类的对象。
文末准备了一份完整系统的进阶提高的技术大纲和学习资料,但愿对于有必定工做经验可是技术还须要提高的朋友提供一个方向参考,以及免去没必要要的网上处处搜资料时间精力。java

特色

  1. 单例类只能有一个示例设计模式

  2. 单例类必须本身建立本身惟一的实例。构造函数是私有的,外部是没法实例化该类。安全

  3. 单例类必须给全部其余对象提供这一实例。多线程

优缺点

  1. 优势
  • 减小程序内部实例数目,节省系统资源并发

  • 全局使用的实例能够避免其频繁的建立与销毁函数

  • 避免对资源的多重占用学习

  1. 缺点
  • 没有接口,不能继承spa

  • 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化线程

实现

  1. 按照概念,咱们实现一下单例模式,以下:
public class SingleInstanceClass {
    //建立本身的对象
    private static SingleInstanceClass instanceClass=new SingleInstanceClass();
    //私有构造方法
    private SingleInstanceClass(){

    }
    //对外提供获取该类对象的方法
    public static SingleInstanceClass getInstanceClass(){
        return instanceClass;
    }
}

复制代码

这就完了吗?设计


那必须接着折腾,上面代码虽然彻底实现了单例。可是会发现,若是程序中没有使用到这个对象,他依然会在编译时建立对应的实例,这样就浪费了资源。因而便有了懒加载的建立单例方式。

  1. 懒加载模式
    按照上面的分析,为了避免浪费资源咱们须要在使用给对象的时候再去建立它的实例对象,也就是懒加载模式。看下面的代码:
public class SingleInstanceClass {
    private static SingleInstanceClass instanceClass;
    //私有构造方法
    private SingleInstanceClass(){

    }
    //对外提供获取该类对象的方法,且调用此方法时,建立实例
    public static SingleInstanceClass getInstanceClass(){
        //保证惟一性
        if(instanceClass==null){
            instanceClass=new SingleInstanceClass();
        }
        return instanceClass;
    }
}
复制代码

与放法一不一样之处在于,只有当咱们使用SingleInstanceClass.getInstanceClass()方法时才会实例化该对象。可是,若是是在多线程中呢?这种方式又有弊端了,多线程有可能依然会屡次实例化这个对象。那为解决这个问题咱们来看第三种方式。

  1. 懒加载,线程安全方式
public class SingleInstanceClass {
    private static SingleInstanceClass instanceClass;
    //私有构造方法
    private SingleInstanceClass(){

    }
    //对外提供获取该类对象的方法,且调用此方法时,建立实例,加入线程锁
    public static synchronized SingleInstanceClass getInstanceClass(){
        //保证惟一性
        if(instanceClass==null){
            instanceClass=new SingleInstanceClass();
        }
        return instanceClass;
    }
}
复制代码

synchronized 同步锁,多线程并发时,同一时间只会执行一个线程。

这种方法由于加了锁,会致使执行效率变低,因而乎为了提升运行效率,且又能保证线程安全。又演变出第四中方式。

  1. 双检锁/双重校验锁(DCL,即double-checked locking)
public class SingleInstanceClass {
    private volatile static SingleInstanceClass instanceClass;
    //私有构造方法
    private SingleInstanceClass(){

    }
    //对外提供获取该类对象的方法,且调用此方法时,建立实例
    public static SingleInstanceClass getInstanceClass(){
        //保证惟一性
        if(instanceClass==null){
          synchronized (SingleInstanceClass.class){
              if(instanceClass==null){
                  instanceClass=new SingleInstanceClass();
              }
          }
        }
        return instanceClass;
    }
}
复制代码

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰以后,那么就具有了两层语义:1. 保证了不一样线程对这个变量进行操做时的可见性,即一个线程修改了某个变量的值,这新值对其余线程来讲是当即可见的。2. 禁止进行指令重排序

除此以外还有其它的方式以下

  1. 登记式/静态内部类
    这种方法,要比上面的方法实现上面简单许多,且效果是同样的。
public class SingleInstanceClass {
    //静态内部类中建立外部类的实例
    private static class SingleHolder {
        private static SingleInstanceClass INSTANCE = new SingleInstanceClass();
    }

    //私有构造方法
    private SingleInstanceClass() {

    }

    public static SingleInstanceClass getInstance() {
        return SingleHolder.INSTANCE;
    }

}
复制代码

经验之谈:通常状况下,不建议使用第 1 种和第 2 种方式,建议使用第 3 种方式。只有在要明确实现 懒加载效果时,才会使用第 5 种登记方式。若是有其余特殊的需求,能够考虑使用第 4 种双检锁方式。


相关文章
相关标签/搜索