Android常用设计模式——单例模式

什么是单例模式?

单例对象必须确保只有一个实例存在,一个类有且只有一个实例,而且自行实例化并向整个系统提供这个实例。

使用场景

确保某个类只有一个对象(实例)的场景,避免产生多个对象消耗过多的资源,例如某个对象创建需要消耗的资源过多,如要访问IO和数据库等资源。

实现

(1)、构造方法不对外开放,一般为private;

(2)、通过一个静态方法或者枚举返回单例类对象;

(3)、确保单例类的对象只有一个,尤其是在多线程环境下;

(4)、确保单例类对象在反序列化时不会重复构建对象。

 示例(列举4种实现单例模式的方法)

(1)、饿汉单例模式

public class Singleton {
   private final static Singleton INSTANCE = new Singleton();

   private Singleton(){}

   public static Singleton getInstance(){
       return INSTANCE;
   }
}

饿汉单例模式解决了多线程并发的问题,因为在加载这个类的时候,就实例化了instance

总结:饿汉单例模式的优点是写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题;缺点是在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

(2)、懒汉模式

懒汉模式是生命一个静态类对象,并且在用户第一次调用getInstance时进行初始化

public class Singleton{

   private static Singleton INSTANCE;

   private Singleton(){}

   public static synchronized Singleton getInstance(){
       if (null == INSTANCE){
           INSTANCE = new Singleton();
       }
       return INSTANCE;
   }
}

懒汉模式使用了一个synchronized关键字,所以getInstance是一个同步方法,这就是上述所说的在多线程情况下保证单例对象唯一的一种手段。

总结:懒汉单例模式的优点是单例只在使用时才会被实例化,在一定程度上节约了资源;缺点是第一次加载时需要及时进行实例化,反应稍慢,最大的问题是每次调用getInstance都进行同步,造成不必要的同步开销,所以一般不建议使用

(3)、Double Check Lock(DCL)单例模式

public class Singleton {

   private static Singleton INSTANCE = null;

   private Singleton() {

   }

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

总结:INSTANCE = new Singleton();这条语句并不是一个原子操作,这句代码最终会被编译成多条汇编指令,大致做了以下三件事:

(i)、给Singleton的实例分配内存

(ii)、调用Singleton()的构造方法,初始化成员字段

(iii)、将INSTANCE对象指向分配的内存空间(此时INSTANCE就不是null)

DCL单例模式的优点是资源利用率高,第一次执行getInstance时单例才会被实例化,效率高;缺点是第一次加载时反应稍慢,也由于Java内存模型的原因偶尔会失败,在高并发环境下也有一定的缺陷。

(4)、静态内部类单例模式

public class Singleton {



   private Singleton() {

   }



   public static class SingletonInstance {

       private static final Singleton INSTANCE = new Singleton();

   }



   public static Singleton getInstance() {

       return SingletonInstance.INSTANCE;

   }

}

总结:第一次加载Singleton类时并不会初始化INSTANCE,只有在第一次调用getInstance方法时才会导致INSTANCE被初始化。因此第一次调用INSTANCE调用getInstance方法会导致虚拟机加载SingletonInstance类,这种方式不仅能确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化,所以这才是被推荐使用的单例模式创建方法。

还有一些其他的方式可以用来创建单例类,比如使用枚举实现单例模式,使用容器实现单例模式。

Android源码中的单例模式

在Android系统中,也有很多地方用到单例模式的,比如LayoutInflater类就是一个使用单例模式的类,还有很多的第三方库,也是用的单例模式,如EventBus等。

总结

单例模式是运用频率很高的模式,但是由于在客户端通常没有高并发的情况,因此选择这种实现方式不会有太大的影响,这里推荐使用上述说的静态内部类的方式去实现单例模式。

单例模式的优点:

(1)、由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建和销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显

(2)、由于单例模式只生产一个实例,所以减少了系统的性能开销,当一个对象的生产需要比较多的资源时,如读取配置、生产其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

(3)、单例模式可以避免对资源的多重占用,例如一个写文件的操作,由于只有一个实例存在内存,避免对同一个资源文件的同时写操作。

(4)、单例模式可以在系统设置全局的访问点,优化和资源共享访问,例如,可以设计一个单例类,负责所有数据的映射关系。

单例模式的缺点:

(1)、单例模式一般没有接口,扩展难度比较高,若要扩展出了修改基本代码基本上没有第二种途径可以实现。

(2)、单例对象如果持有Context,那么很容易发生内存泄漏,此时需要注意传递给单例对象的Context最好是Application Context。

欢迎大家加群技术交流:367685933

猜你喜欢

转载自blog.csdn.net/weixin_42600182/article/details/81329483