[创建型模式]单例模式(Singleton)

  • 一个类只能生成一个对象,所有对象对它的依赖都是相同的。
  • 对象的生产是通过 new关键字完成的(当然也有其他的方式,如对象复制,反射等)。
  • 使用new关键字创建对象时,都会根据输入的参数调用相应的构造函数,如果我们把构造函数设置为private私有访问权限时就可以禁止外部创建对象了。
臣子叩拜皇帝类图

代码清单

//皇帝类
public class Emperor {
  private static final Emperor emperor = new Emperor();
  //私有的构造函数
  private Emperor(){

  }
  //创建方法
  public static Emperor getInstance(){
    return emperor;
  }

  public void say(){
    system.out.println("My is Emperor");
  }
}

通过定义一个私有访问权限的构造函数,避免被其他类new出一个对象,而Emperor自己则可以new一个对象出来,其他类对该类的访问都可以通过getInstance获得同一个对象。

//臣子类
public class Minister {
  public static void main(String [] args) {
    for(int day=0; day<3 ; day++){
      Emperor emperor = Emperor.getInstance();
      emperor.say();
    }
  }
}

单例模式定义

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

单例模式的优点

  • 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
  • 由于单例模式只生成一个实例,所以减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
  • 单例模式可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
  • 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

单例模式的缺点

  • 单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何意义的,它要求自行实例化,并且提供单一实例、接口或抽象类是不可能被实例化的。当然,在特殊的情况下,单例模式可以实现接口、被继承等,需要在系统开发中根据环境判断。
  • 单例模式对测试是不利的,在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
  • 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要单例取决于环境,单例模式把要单例和业务逻辑融合在一个类中。

使用场景

在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现不良反应,可以采用单例模式。

  • 要求生成唯一序列号的环境。
  • 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的。
  • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源。
  • 需要定义大量的静态常量和静态方法的环境,可以采用单例模式。

注意事项

首先,在高并发情况下,请注意单例模式的线程同步问题。之前的例子不会出现产生多个实例的情况,但是下面的例子就需要考虑线程同步。

�public class Singleton {
  private static Singleton singleton = null;
  private Singleton() {

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

该单例模式在低并发的情况下尚不会出现问题,若系统压力增大,并发量增加时则可能在内存中出现多个实例,破坏了最初预期。为什么会出现这种情况呢?如果一个线程A执行到singleton = new Singleton(),但还没有获得对象(对象初始化是需要时间的),第二个线程B也在执行,执行到if(singleton == null)判断,那么线程B获得判断条件也是为真,于是内存中就出现两个对象。

解决线程不安全的方法有很多,可以在getInstance方法前加synchronized关键字,也可以在getInstance方法内增加synchronized来实现。

其次,需要考虑对象的复制情况。在Java中,对象默认是不可以被复制的,若实现了Cloneable接口,并实现了clone方法,则可以直接通过对象复制方式创建一个新对象,对象复制是不用调用类的构造函数,因此即使是私有的构造函数,对象仍然可以被复制。在一般情况下,类复制的情况不需要考虑,很少会出现一个单例类会主动要求被复制的情况,解决该问题最好的方法就是单例类不要实现Cloneable接口。

单例模式的扩展

需要产生固定数量对象的模式就叫做有上限的多例模式,它是单例模式的一种扩展,采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提供系统的响应速度。

//皇帝类
public class Emperor {
  private static int maxNumOfEmperor = 2;
  private static ArrayList<String> nameList = new ArrayList<String>();
  private static ArrayList<Emperor> emperorList = new ArrayList<Emperor>();
  private static int countNumOfEmperor = 0;
  static {
    for(int i=0; i<maxNumOfEmperor; i++){
      emperorList.add(new Emperor("皇"+(i+1)+"帝"));
    }
  }
  //私有的构造函数
  private Emperor(){

  }
  private Emperor(String name){
    nameList.add(name);
  }
  //创建方法
  public static Emperor getInstance(){
    Random random = new Random();
    countNumOfEmperor = random.nextInt(maxNumOfEmperor);
    return emperorList.get(countNumOfEmperor);
  }

  public void say(){
    system.out.println("My is Emperor");
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 读取配置文件的内容## 考虑这样一个应用,读取配置文件的内容。 很多应用项目,都有与应用相...
    七寸知架构阅读 6,616评论 12 68
  • 前言 本文主要参考 那些年,我们一起写过的“单例模式”。 何为单例模式? 顾名思义,单例模式就是保证一个类仅有一个...
    tandeneck阅读 2,481评论 1 8
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 单例模式(SingletonPattern)一般被认为是最简单、最易理解的设计模式,也因为它的简洁易懂,是项目中最...
    成热了阅读 4,213评论 4 34
  • 我的大学同学念文与班上的安真绝交了,跟我们也渐渐疏远了,原因我们大家都心知肚明。现在想想,大家还是不胜唏嘘。 那一...
    沙小沙阅读 1,470评论 0 6