单例模式是在平时的项目开发中比较常见的一种设计模式,使用比较普遍,网上的资料也是一抓一大把,小Alan也来凑凑热闹,为以后充实点设计模式相关的内容做个简单的开篇。

单例模式是一种创建对象的模式,用于产生这个类的一个具体的实例对象,跟普通的对象创建比起来就那么一点点区别,区别就在于它可以确保项目中的一个类只会产生一个具体的对象实例。而不会出现第二个对象实例,第三个对象实例。所有使用到这个对象实例的地方实际上用的都是同一个对象,这就是所谓的单例模式,对于初学者还可能陌生,对于老司机们来讲这可是最最简单的设计模式之一。

在Java中使用单例模式有哪些好处嘞?

①对于频繁使用、经常使用的对象,可以省略创建对象也就是没对象的需要new一个对象所花费的时间,这对于那些重量级的对象来说,还是能少来一个就少来一个,用老对象就好了,减少开销;

②创建对象的次数变少了,对系统内存使用的频率就会降低,这样就减轻了GC的压力,缩短了GC所耗费的时间;

③开发项目的过程中遇到类只需要一个对象实例的时候,那么就是选择这模式无疑了。

单例的实现1:

下面给出一个单例的实现,这个实现是so easy的,代码如下:

  1. /**
  2. * 单例模式
  3. * @author AlanLee
  4. *
  5. */
  6. public class Singleton
  7. {
  8.  
  9. // 饿汉模式
  10. private static Singleton instance = new Singleton();
  11.  
  12. private Singleton()
  13. {
  14. System.out.println("Singleton is create");
  15. }
  16.  
  17. public static Singleton getInstance()
  18. {
  19. return instance;
  20. }
  21.  
  22. }

使用这种方式创建单例对象有几点需要特别注意。

第一点:我们要保证我们的项目中不会有人意外的创建多余的对象实例的话,我们需要把Singleton的构造函数设置为private私有的。这样其他开发人员就不能随便的创建这个类的对象实例了,从而避免该类的对象实例被错误的创建出来;

第二点:instance对象必须是private私有的并且static静态化的。如果不是private私有的,那么instance的安全性无法得到保证。一不小心可能就被其他开发人员来个Singleton.instance=xxx的,那这个对象也就被改变了,如果=null的话,可想而知,在使用这个对象实例的使用,迎接你的将是空对象异常的怀抱。其次,因为工厂方法getInstance()是static静态方法,因此方法中返回的变量也得是static的。

探讨:每种实现方式在高并发环境下性能如何呢,每种实现方式有木有什么不足之处?

这个单例模式的实现方式性能是非常好的,因为工厂方法getInstance()只是简单的返回instance对象实例,并没有任何锁操作,因此在并行程序中,还是会有比较不错的表现滴。

但是这种方式有一点不足,就是instance对象实例在什么时候被创建出来是不受控制的,基础好点的都知道static成员会在类第一次初始化的时候被创建,这个时候可不一定是工厂方法getInstance()第一次被调用的时候。

假设你的单例模式是这样的,代码如下:

  1. /**
  2. * 单例模式:实例对象第一次初始化的问题
  3. *
  4. * @author AlanLee
  5. *
  6. */
  7. public class Singleton2
  8. {
  9. public static int STATUS = 1;
  10.  
  11. private static Singleton2 instance = new Singleton2();
  12.  
  13. private Singleton2()
  14. {
  15. System.out.println("Singleton2 is create");
  16. }
  17.  
  18. public static Singleton2 getInstance()
  19. {
  20. return instance;
  21. }
  22.  
  23. }

注意,这个单例还包含另一个静态成员STATUS。此时,在任何地方引用这个STATUS都会导致instance对象实例被创建(任何对Singleton2方法或者字段的引用,都会导致类初始化,并创建instance实例,但是类初始化只有一次,因此instance实例永远只会被创建一次)。

比如:System.out.println(Singleton.STATUS);

上述println会打印出:

可以看到,就算我们没有要求创建instance单例对象,new Singleton2()也会被调用。

如果不在乎这个小小的不足之处,这种单例模式的实现方式是一种不错的选择。它容易实现,代码易读而且性能优越。

单例的实现2:

如果你想精准的控制instance的创建时间,那么就需要使用下面这种方式,一种支持延迟加载的策略,它只会在instance被第一次使用时才会创建对象。代码如下:

  1. /**
  2. * 单例模式之懒汉模式
  3. *
  4. * @author AlanLee
  5. *
  6. */
  7. public class LazySingleton
  8. {
  9. private static LazySingleton instance = null;
  10.  
  11. private LazySingleton()
  12. {
  13. System.out.println("LazySingleton is create");
  14. }
  15.  
  16. public static synchronized LazySingleton getInstance()
  17. {
  18. if (instance == null)
  19. {
  20. instance = new LazySingleton();
  21. }
  22. return instance;
  23. }
  24. }

最初我们并不需要实例化instance对象实例,只有工厂方法getInstance()被第一次调用时才会创建单例对象。但是在高并发环境下,为了防止对象被对此创建,我们不得不使用synchronized进行方法同步。这种实现的好处是,充分利用了延迟加载,只有在真正需要时才创建对象。但坏处也很明显,并发环境下加锁,在锁竞争激烈的时候会对性能产生一定的影响。

此外,还有一种被称为双重检查模式的方法可以用于创建单例。这是一种非常丑陋、复杂的方法,甚至在低版本的JDK中都不能保证正确性。不推荐使用,也没必要在这种方法上花费太多时间。

单例的实现3:

在上述的单例模式实现方式中,可说是各有千秋,那么第三种方式便是结合两者的优势的一种两全其美的实现方式,代码如下:

  1. /**
  2. * 无懈可击之单例模式
  3. *
  4. * @author Alanlee
  5. *
  6. */
  7. public class StaticSingleton
  8. {
  9.  
  10. private StaticSingleton()
  11. {
  12. System.out.println("StaticSingleton is create");
  13. }
  14.  
  15. private static class SingletonHolder
  16. {
  17. private static StaticSingleton instance = new StaticSingleton();
  18. }
  19.  
  20. public static StaticSingleton getInstance()
  21. {
  22. return SingletonHolder.instance;
  23. }
  24.  
  25. }

上述代码实现了一个单例模式,并且同时拥有前两种方式的优点。首先工厂方法getInstance()没有使用同步锁,这使得在高并发环境下性能得到了提升。其次,只有在工厂方法getInstance()被第一次调用时,StaticSingleton的实例才会被创建。这种方式巧妙地使用了内部类和类的初始化方式。内部类SingletonHolder被申明为private私有的,这使得我们不可能在外部访问并初始化它。而我们只能在工厂方法getInstance()内部对SingletonHolder类进行初始化,利用虚拟机的类初始化机制创建单例对象。

结束语:宠辱不惊,闲看庭前花开花落;去留无意,漫随天外云卷云舒......小Alan除了喜欢看技术书籍,还是一个武侠玄幻小说爱好者呢!希望自己在IT的这条道路上就能像小说中的主人公一样,纵然困难重重,亦能化险为夷成就康庄大道,至于坐拥美女环抱啥的,小Alan可不想啊,还是有一个深爱的女人足以。

可爱博主:AlanLee

博客地址:http://www.cnblogs.com/AlanLee

本文出自博客园,欢迎大家加入博客园。

Java设计模式探讨之单例模式的更多相关文章

  1. Java设计模式之《单例模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6510196.html 所谓单例,指的就是单实例,有且仅有一个类实例,这个单例不应该 ...

  2. Java设计模式之【单例模式】

    Java设计模式之[单例模式] 何为单例 在应用的生存周期中,一个类的实例有且仅有一个 当在一些业务中需要规定某个类的实例有且仅有一个时,就可以用单例模式 比如spring容器默认初始化的实例就是单例 ...

  3. Java设计模式中的单例模式

    有时候在实际项目的开发中,我们会碰到这样一种情况,该类只允许存在一个实例化的对象,不允许存在一个以上的实例化对象,我们将这种情况称为Java设计模式中的单例模式.设计单例模式主要采用了Java的pri ...

  4. 重学 Java 设计模式:实战单例模式

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 5个创建型模式的最后一个 在设计模式中按照不同的处理方式共包含三大类:创建型模式.结 ...

  5. Java设计模式4:单例模式

    前言 非常重要,单例模式是各个Java项目中必不可少的一种设计模式.本文的关注点将重点放在单例模式的写法以及每种写法的线程安全性上.所谓"线程安全性"的意思就是保证在创建单例对象的 ...

  6. 10.Java设计模式 工厂模式,单例模式

    Java 之工厂方法和抽象工厂模式 1. 概念 工厂方法:一抽象产品类派生出多个具体产品类:一抽象工厂类派生出多个具体工厂类:每个具体工厂类只能创建一个具体产品类的实例. 即定义一个创建对象的接口(即 ...

  7. Java设计模式学习01——单例模式(转)

    原地址:http://blog.csdn.net/xu__cg/article/details/70182988 Java单例模式是一种常见且较为简单的设计模式.单例模式,顾名思义一个类仅能有一个实例 ...

  8. 【java设计模式】-04单例模式

    单例模式 定义: 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 类型: 创建类模式 类图: 单例模式特点 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单 ...

  9. Java设计模式09:单例模式的强化(控制实例个数n)

    1. 单例模式的本质: 控制实例数目(目的节约资源) 2. 单例模式体现的一些思想: (1)延迟装载(Lazy Load):懒汉式 (2)缓存:饿汉式 3. 单例模式的变形使用: 控制使用实例个数为3 ...

随机推荐

  1. 一文搞懂各种 Docker 网络 - 每天5分钟玩转 Docker 容器技术(72)

    前面各小节我们先后学习了 Docker Overaly,Macvaln,Flannel,Weave 和 Calico 跨主机网络方案.目前这个领域是百家争鸣,而且还有新的方案不断涌现. 本节将从不同维 ...

  2. go-fasthttp源码分析

    1.架构 listener->server->workerpool 1.1.workerpool中有两种缓存: a.wp.ready,缓存未退出worker, b.worker退出后用sy ...

  3. Opengl4.5 中文手册—A

    因为opengl API 比较庞大,网络上还没有完整的.较新的opengl中文手册 这对很多人很不方便,所以整理了这一系列,用于帮助大家"快速浏览最新的opengl api" 为了 ...

  4. JAVA 一步一步向上爬

    Java分为基本数据类型和引用数据类型(类.接口.数组) Integer.MAX_VALUE 浮点型默认为double java采用Unicode char为两个字节 Unicode为每一个字符定制了 ...

  5. Android中Parcelable接口

    1. Parcelable接口 Interface for classes whose instances can be written to and restored from a Parcel. ...

  6. crontab的两大坑:百分号和环境变量

    今天想给服务器加个自动备份mysql数据库的功能(别怪我这么久才加,阿里云每天全盘备份的,不怕丢数据库),本以为只要5分钟就能搞定的,结果入了两个大坑. 我的crontab是这样写的: * * * m ...

  7. [Oracle]理解undo表空间

    一.回退段介绍 在Oracle数据库中,当某个事物对数据进行修改时,Oracle首先将数据的原始值保存到一个回退段中.一个事物只能将它的回退信息保存到一个回退段中,而多个并行事物可以使用同一个回退段. ...

  8. 基于maven创建和部署Webx项目

    1.准备工作 下载 Webx Maven 项目的目录结构Artifact插件. archetype-webx-quickstart-1.0.tar.gz插件:http://central.maven. ...

  9. Java中的类型擦除与桥方法

    类型擦除 Java在语法中虽然存在泛型的概念,但是在虚拟机中却没有泛型的概念,虚拟机中所有的类型都是普通类.无论何时定义一个泛型类型,编译后类型会被都被自动转换成一个相应的原始类型. 比如这个类 pu ...

  10. ch3-form(get/post) $.ajax(get/post)

    1 http(get)请求 提交的数据 用req.query接收 1.1 router.get() //http(get)请求方式 1.2 接收http(get)方式提交的数据 req.query 方 ...