热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要介绍了单例模式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. 总结

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

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


推荐阅读
  • 在计算机技术的学习道路上,51CTO学院以其专业性和专注度给我留下了深刻印象。从2012年接触计算机到2014年开始系统学习网络技术和安全领域,51CTO学院始终是我信赖的学习平台。 ... [详细]
  • Valve 发布 Steam Deck 的新版 Windows 驱动程序
    Valve 最新发布了针对 Steam Deck 掌机的 Windows 驱动程序,旨在提升其在 Windows 环境下的兼容性、安全性和性能表现。 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 本文总结了汇编语言中第五至第八章的关键知识点,涵盖间接寻址、指令格式、安全编程空间、逻辑运算指令及数据重复定义等内容。通过详细解析这些内容,帮助读者更好地理解和应用汇编语言的高级特性。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 数据管理权威指南:《DAMA-DMBOK2 数据管理知识体系》
    本书提供了全面的数据管理职能、术语和最佳实践方法的标准行业解释,构建了数据管理的总体框架,为数据管理的发展奠定了坚实的理论基础。适合各类数据管理专业人士和相关领域的从业人员。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 优化版Windows 10 LTSC 21H2企业版:适用于低内存设备
    此版本为经过优化的Windows 10 LTSC 21H2企业版,特别适合低内存配置的计算机。它基于官方版本进行了精简和性能优化,确保在资源有限的情况下依然能够稳定运行。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 当iOS设备越狱后,某些插件可能会导致系统崩溃(白苹果)。此时,可以通过进入安全模式来排查并删除有问题的插件。本文将详细介绍如何通过特定按键组合进入不加载MobileSubstrate的安全模式,并提供相关背景知识。 ... [详细]
  • 使用C#开发SQL Server存储过程的指南
    本文介绍如何利用C#在SQL Server中创建存储过程,涵盖背景、步骤和应用场景,旨在帮助开发者更好地理解和应用这一技术。 ... [详细]
  • 新冠肺炎疫情期间,各大银行积极利用手机银行平台,满足客户在金融与生活多方面的需求。线上服务不仅激活了防疫相关的民生场景,还推动了银行通过互联网思维进行获客、引流与经营。本文探讨了银行在找房、买菜、打卡、教育等领域的创新举措。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
author-avatar
福州-台江_616
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有