Android 单例模式 Singleton 简单实例设计模式解析

单例模式 Singleton 简单实例设计模式解析

前言

今天我来全面总结一下Android开发中最常用的设计模式 - 单例模式。

关于设计模式的介绍,可以看下我之前写的:1分钟全面了解“设计模式”

目录

1. 引入

1.1 解决的是什么问题

之前说过,设计模式 = 某类特定问题的解决方案,那么单例模式是解决什么问题的解决方案呢?

含义:单例 =一个实例;

解决的问题:降低对象之间的耦合度

解决方法:单例模式,即实现一个类只有一个实例化对象,并提供一个全局访问点

1.2 实例引入

接下来我用一个实例来对单例模式进行引入

背景:小成有一个塑料生产厂,但里面只有一个仓库。

目的:想用代码来实现仓库的管理

现有做法: 建立仓库类和工人类

其中,仓库类里的quantity=商品数量;工人类里有搬运方法MoveIn(int i)和MoveOut(int i)。

出现的问题:通过测试发现,每次工人搬运操作都会新建一个仓库,就是货物都不是放在同一仓库,这是怎么回事呢?(看下面代码)

package scut.designmodel.SingletonPattern;

//仓库类
class StoreHouse {
  private int quantity = 100;

  public void setQuantity(int quantity) {
    this.quantity = quantity;
  }

  public int getQuantity() {
    return quantity;
  }
}

//搬货工人类
class Carrier{
  public StoreHouse mStoreHouse;
  public Carrier(StoreHouse storeHouse){
    mStoreHouse = storeHouse;
  }
  //搬货进仓库
  public void MoveIn(int i){
    mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
  }
  //搬货出仓库
  public void MoveOut(int i){
    mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
  }
}

//工人搬运测试
public class SinglePattern {
  public static void main(String[] args){
    StoreHouse mStoreHouse1 = new StoreHouse();
    StoreHouse mStoreHouse2 = new StoreHouse();
    Carrier Carrier1 = new Carrier(mStoreHouse1);
    Carrier Carrier2 = new Carrier(mStoreHouse2);

    System.out.println("两个是不是同一个?");

    if(mStoreHouse1.equals(mStoreHouse2)){//这里用equals而不是用 == 符号,因为 == 符号只是比较两个对象的地址
      System.out.println("是同一个");
    }else {
      System.out.println("不是同一个");
    }
    //搬运工搬完货物之后出来汇报仓库商品数量
    Carrier1.MoveIn(30);
    System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity());
    Carrier2.MoveOut(50);
    System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity());
  }
}

结果:

两个是不是同一个?
不是同一个
仓库商品余量:130
仓库商品余量:50

2. 单例模式介绍

2.1 解决的问题(应用场景)

冲突:从上面的结果可以看出,工人类操作的明显不是同一个仓库实例。

目标:全部工人操作的是同一个仓库实例

单例模式就是为了解决这类问题的解决方案:实现一个类只有一个实例化对象,并提供一个全局访问点2.2 工作原理

在Java中,我们通过使用对象(类实例化后)来操作这些类,类实例化是通过它的构造方法进行的,要是想实现一个类只有一个实例化对象,就要对类的构造方法下功夫:

单例模式的一般实现:(含使用步骤)

public class Singleton {
//1. 创建私有变量 ourInstance(用以记录 Singleton 的唯一实例)
//2. 内部进行实例化
  private static Singleton ourInstance = new Singleton();

//3. 把类的构造方法私有化,不让外部调用构造方法实例化
  private Singleton() {
  }
//4. 定义公有方法提供该类的全局唯一访问点
//5. 外部通过调用getInstance()方法来返回唯一的实例
  public static Singleton newInstance() {
    return ourInstance;
  }
}

好了,单例模式的介绍和原理应该了解了吧?那么我们现在来解决上面小成出现的“仓库不是一个”的问题吧!

2.3 实例介绍

小成使用单例模式改善上面例子的代码:

package scut.designmodel.SingletonPattern;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//单例仓库类
class StoreHouse {

  //仓库商品数量
  private int quantity = 100;
  //自己在内部实例化
  private static StoreHouse ourInstance = new StoreHouse();;
  //让外部通过调用getInstance()方法来返回唯一的实例。
  public static StoreHouse getInstance() {
    return ourInstance;
  }

  //封闭构造函数
  private StoreHouse() {
  }

  public void setQuantity(int quantity) {
    this.quantity = quantity;
  }

  public int getQuantity() {
    return quantity;
  }
}

//搬货工人类
class Carrier{
  public StoreHouse mStoreHouse;
  public Carrier(StoreHouse storeHouse){
    mStoreHouse = storeHouse;
  }
  //搬货进仓库
  public void MoveIn(int i){
    mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i);
  }
  //搬货出仓库
  public void MoveOut(int i){
    mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i);
  }
}

//工人搬运测试
public class SinglePattern {
  public static void main(String[] args){
    StoreHouse mStoreHouse1 = StoreHouse.getInstance();
    StoreHouse mStoreHouse2 = StoreHouse.getInstance();
    Carrier Carrier1 = new Carrier(mStoreHouse1);
    Carrier Carrier2 = new Carrier(mStoreHouse2);

    System.out.println("两个是不是同一个?");

    if(mStoreHouse1.equals(mStoreHouse2)){
      System.out.println("是同一个");
    }else {
      System.out.println("不是同一个");
    }
    //搬运工搬完货物之后出来汇报仓库商品数量
    Carrier1.MoveIn(30);
    System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity());
    Carrier2.MoveOut(50);
    System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity());
  }
}

结果:

两个是不是同一个?
是同一个
仓库商品余量:130
仓库商品余量:80

从结果分析,使用了单例模式后,仓库类就只有一个仓库实例了,再也不用担心搬运工人进错仓库了!!!

2.4 优点

  1. 提供了对唯一实例的受控访问;
  2. 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能;
  3. 可以根据实际情况需要,在单例模式的基础上扩展做出双例模式,多例模式;

2.5 缺点

  1. 单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单一职责原则”。
  2. 如果实例化的对象长时间不被利用,会被系统认为是垃圾而被回收,这将导致对象状态的丢失。

3. 单例模式的实现方式

3.1 一般情况

饿汉式(最简单的单例实现方式)

class Singleton {
  private static Singleton ourInstance = new Singleton();

  private Singleton() {
  }

  public static Singleton newInstance() {
    return ourInstance;
  }
}

应用场景:

  • 要求直接在应用启动时加载并初始化
  • 单例对象要求初始化速度非常快且占用内存非常小

懒汉式

懒汉式与饿汉式最大的区别是单例的初始化操作的时机

  • 饿汉式:自动进行单例的初始化
  • 懒汉式:有需要的时候才手动调用newInstance()进行单例的初始化操作
class Singleton {
  private static Singleton ourInstance = null;

  private Singleton() {
  }

  public static Singleton newInstance() {
  if( ourInstance == null){
    ourInstance = new Singleton();
    }
    return ourInstance;
  }
}

应用场景:

  • 单例初始化的操作耗时比较长而应用对于启动速度又有要求
  • 单例的占用内存比较大
  • 单例只是在某个特定场景的情况下才会被使用,即按需延迟加载单例。

3.2 多线程下的单例模式实现

在多线程的情况下:

  • 对于“饿汉式单例模式”:适用,因为JVM只会加载一次单例类;
  • 对于“懒汉式单例模式”:不适用,因为“懒汉式”在创建单例时是线程不安全的,多个线程可能会并发调用 newInstance 方法从而出现重复创建单例对象的问题。

解决方案1:同步锁

使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成instance被多次实例化。

class Singleton {
  private static Singleton ourInstance = null;

  private Singleton() {
  }

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

解决方案2:双重校验锁

在同步锁的基础上( synchronized (Singleton.class) 外)添加了一层if,这是为了在Instance已经实例化后下次进入不必执行 synchronized (Singleton.class) 获取对象锁,从而提高性能。

class Singleton {
  private static Singleton ourInstance = null;

  private Singleton() {
  }

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

解决方案3:静态内部类

在JVM进行类加载的时候会保证数据是同步的,我们采用内部类实现:在内部类里面去创建对象实例。
只要应用中不使用内部类 JVM 就不会去加载这个单例类,也就不会创建单例对象,从而实现“懒汉式”的延迟加载和线程安全。

class Singleton {

  //在装载该内部类时才会去创建单例对象
  private static class Singleton2{
   private static Singleton ourInstance = new Singleton();
  }
  private Singleton() {
  }

  public static Singleton newInstance() {
    return Singleton2.ourInstance;
  }
}

解决方案4:枚举类型

最简洁、易用的单例实现方式,(《Effective Java》推荐)

public enum Singleton{

  //定义一个枚举的元素,它就是Singleton的一个实例
  instance;

  public void doSomething(){
  }  

}

使用方式如下:

Singleton singleton = Singleton.instance;
singleton.doSomething();

5. 总结

本文主要对单例模式进行了全面介绍,包括原理和实现方式,接下来我会继续讲解其他设计模式,有兴趣可以继续关注

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • Android源码学习之单例模式应用及优点介绍

    单例模式定义: Ensure a class has only one instance, and provide a global point of access to it. 动态确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 如上图所示(截取自<Head First Design Patterns>一书). 通过使用private的构造函数确保了在一个应用中产生一个实例,并且是自行实例化(在Singleton中自己使用new Singleton()). 具体单例模式有

  • Android单例模式的几种方法总结

     Android单例模式的几种方法总结 因为单例模式过于简单,下面我就直接上代码了. 简单式: public class Single{ private static Single single=new Single(); public static Single instance(){ return singlel; } } 复杂式: public class Single{ private static Single single: public static Single instance

  • android开发设计模式之——单例模式详解

    单例模式是设计模式中最常见也最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局的访问到.比如在Android实际APP 开发中用到的 账号信息对象管理, 数据库对象(SQLiteOpenHelper)等都会用到单例模式.下面针对一些例子分析一下我们在开发过程中应用单例模式需要注意的点.  一.作用 单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点 二.适用场景 1. 应用中某个实例对象需要频繁的被访问. 2. 应用中每次启动只会存在一个实例.如账号

  • Android设计模式之单例模式解析

    在日常开发过程中时常需要用到设计模式,但是设计模式有23种,如何将这些设计模式了然于胸并且能在实际开发过程中应用得得心应手呢?和我一起跟着<Android源码设计模式解析与实战>一书边学边应用吧! 今天我们要讲的是单例模式 定义 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例 使用场景 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源 某个类型的对象只应该有一个 使用例子 应用的Application 图片加载框架对象,比如我们的ImageLoader,常用的

  • android设计模式之单例模式详解

    这是我们最常见的一类模式,对这一类模式有一个通用的特点就是: 封装创建的方式和过程. 这里所谓封装就是隐藏的意思,对对象的创建方法和过程不可见,或者是虚拟的过程. 隐藏创建方式,就是如单例,工厂方法,隐藏创建过程则是指builder,原型,至于抽象工厂,我认为他包含了以上两种. 我们想想一个对象的创建有哪些步骤? 1.创建什么东西?--接口定义 2.谁创建?        --决策类or帮助类 3.如何创建?     --how,创建过程 4.什么时候创建?    --创建时机的触发 由此可知,

  • Android设计模式系列之单例模式

    单例模式,可以说是GOF的23种设计模式中最简单的一个. 这个模式相对于其他几个模式比较独立,它只负责控制自己的实例化数量单一(而不是考虑为用户产生什么样的实例),很有意思,是一个感觉上很干净的模式,本人很喜欢这个模式. android中很多地方都用到了单例模式,本文以输入法管理者InputMethodManager为例,展开分析. 单例模式,Singleton Pattern,能够以其特有的优势,替代系统中全局变量,应用非常广泛. 1.意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点

  • Android 单例模式 Singleton 简单实例设计模式解析

    单例模式 Singleton 简单实例设计模式解析 前言 今天我来全面总结一下Android开发中最常用的设计模式 - 单例模式. 关于设计模式的介绍,可以看下我之前写的:1分钟全面了解"设计模式" 目录 1. 引入 1.1 解决的是什么问题 之前说过,设计模式 = 某类特定问题的解决方案,那么单例模式是解决什么问题的解决方案呢? 含义:单例 =一个实例: 解决的问题:降低对象之间的耦合度 解决方法:单例模式,即实现一个类只有一个实例化对象,并提供一个全局访问点 1.2 实例引入 接下

  • Android 自定义ContentProvider简单实例

    Android 自定义ContentProvider简单实例 Android允许我们定义自己的的ContentProvider对象来共享数据,练练手,简单来实现一下. 要使用ContentProvider来操作数据,必须要有保存数据的场所.可以使用文件或SQLite数据库的方式来保存数据,通常使用SQLite数据库. 1,创建一个数据库帮助类,归根结底都是它在操作数据库.代码如下: package com.njue; import android.content.Context; import

  • Android 登录处理简单实例(源码下载)

    Android 登录处理简单实例 今天整理一下之前在项目中写的关于某些界面需要登录判断处理.这里整理了一个简易的 Demo 模拟一下 登录情况 和 未登录情况 下的界面跳转处理, 效果如图: 以上分别模拟了,未登录和已登录 情况下的 界面跳转和当前界面事件处理.接下来我们来看一下实现方式. 先从核心代码开始看: public class GoLoginUtil { public static final String CLASSNAME = "className"; public st

  • C#单例模式(Singleton Pattern)实例教程

    本文以实例形式讲述了C#单例模式(Singleton Pattern)的实现方法,分享给大家供大家参考.具体实现方法如下: 一般来说,当从应用程序全局的角度来看,如果只允许类的一个实例产生,就可以考虑单例模式. 1.即时加载的单例模式 把类的实例赋值给类的一个静态字段. class Program { static void Main(string[] args) { Logger log = Logger.GetInstance(); log.WriteToFile(); Console.Re

  • Android手势操作简单实例讲解

    上一篇介绍的onTouch提供的事件还是相对较简单,如果需要处理一些复杂的手势,用这个接口就会很麻烦,因为我们要根据用户触摸的轨迹去判断是什么手势.幸好Android SDK给我们提供了GestureDetector类,通过这个类我们可以识别很多的手势,主要是通过他的onTouchEvent(event)方法完成了不同手势的识别. GestureDetector这个类对外提供了两个接口和一个外部类:  •接口:OnGestureListener,OnDoubleTapListener  •外部类

  • Android 多媒体播放API简单实例

    本文调用android的媒体播放器实现一些音乐播放操作 项目布局: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="matc

  • Java设计模式单例模式(Singleton)用法解析

    这篇文章主要介绍了Java设计模式单例模式(Singleton)用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 单例模式的应用场景: 单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例.并提供一个全局反访问点.单例模式是创建型模式.单例模式在生活中应用也很广泛,比如公司CEO只有一个,部门经理只有一个等.JAVA中ServletCOntext,ServetContextCOnfig等,还有spri

  • 解析Javascript单例模式概念与实例

    前言 和其他编程语言一样,Javascript同样拥有着很多种设计模式,比如单例模式.代理模式.观察者模式等,熟练运用Javascript的设计模式可以使我们的代码逻辑更加清晰,并且更加易于维护和重构. 本文将介绍Javascript模式中较为常见和实用的模式--单例模式,主要分为概念和实例部分.在介绍实例的同时也会对代码中额外的知识点进行讲解. 单例模式概念 首先什么是单例模式?可以这样理解:单例模式旨在保证一个类仅有一个实例,并提供一个全局的访问点. 可能有人还是不太理解单例的概念,那么你可

  • spring ioc的简单实例及bean的作用域属性解析

    IoC(Inversion if Control)-控制反转是Spring俩大核心技术之一,IoC一般分为俩种类型:依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup) 使用示例: 1.新建工程并导入Spring相关jar包. 2.新建数据访问层及业务逻辑层 代码结构: 代码示例: /** * 实体Bean * @author BC * */ public class User { private Integer id; private

  • .Net设计模式之单例模式(Singleton)

    一.动机(Motivation) 在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性.以及良好的效率. 如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例? 这应该是类设计者的责任,而不是使用者的责任. 二.意图(Intent) 保证一个类仅有一个实例,并提供一个该实例的全局访问点 三.结构(Structure) 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 四.单例模式的代码实现 1.单线程Singleton模式的实现 publ

随机推荐