设计模式学习01—单例模式

一、动机与定义

系统中有些资源只能有一个,或者一个就够,多个浪费。例如一个系统只能有一个窗口管理器或文件系统、一个系统只能有一个计时器或序号生成器、web系统只能有一个页面计数器等等。此时,最好就需要把这些资源设置成有且仅有一个实例。

代码中也就是如何保证一个类只有一个实例并且这个实例能够被访问呢?只有一个实例的就意味着不能让其他类来实例化,也就是只能自己实例化自己。能够被访问也就意味着自身要对外提供全局方法来获取到这个实例,这就是单例模式。

单例模式定义:确保某一个类只有一个实例,而且自行实例化并且向整个系统提供这个实例。

单例模式通常代表着系统具有唯一性的资源。主要有3点:只有一个实例;自行创建这个实例;自行向整个系统提供这个实例。

二、结构与类图

单例模式是创建型模式,其实结构非常简单,需要注意以下3点:

1、构造方法私有:不让外部实例化,只能将构造函数私有;

2、提供一个公共静态方法获取实例:获取这个实例前是没有实例的,只能用静态的。

3、实例保存到自身静态私有属性上:获取方法是静态的,实例当然也只能是静态的,最好是final的,单例不允许修改;

通用类图如下:

代码如下:

public class Singleton {

    private static final Singleton instance = new Singleton();

    private Singleton() {

    }

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

三、适用场景及效果(优缺点)

1、只需要1个实例,多了浪费,主要用于节约系统资源,创建一个对象需要消耗过多资源时,考虑将这个对象缓存,设计成单例的,如创建某些程序启动配置对象读取、操作系统的文件系统等,只需要创建一个就够了,多了浪费;

2、只需要1个实例,多了出错,如计数器,唯一序列号生成器等;

3、单例意味着多线程使用(如果单线程使用,单例完全没有意义了),多线程下可以控制单一共享资源的访问和线程间通讯,避免对同一资源的多重占用,如仅有1个打印机,各个线程自行调用会对一个资源多重占用,单例模式可以统一管理对打印机的访问,还有如数据库连接池、线程池、日志应用等。

4、大量无状态的类实例,如需要大量静态常量或方法(有时也可以定义成static)可以考虑使用单例模式,如web开发中的service层,都是业务无状态的逻辑处理类,还有工具类和方法等,都可以设计成单例模式,这也是Spring框架中配置的bean默认都是单例的。

优点(使用后的效果):

1、单例只有一个实例,也只创建一次,可以节约系统资源,特别当这个对象需要频繁地创建和销毁时,而且创建和销毁要比较多的资源时;

2、能避免对单一资源的多重占用,进行统一管理。

3、单例模式可以在系统设置全局访问点,优化和共享资源访问。

缺点:

1、没有接口,扩展困难,无法适应变化,基本上只能修改源码。(为什么没接口,就一个实例,接口没意义);

2、测试麻烦,单例没完成,无法测试;

3、与单一职责冲突。

单例模式可以分为有状态的和无状态的,无状态的单例对象不可变的,一般就是提供一些工具方法,有状态的单例对象是可变的,常用来给系统当作状态库,提供一些状态,如序列号生成器等。

四、示例

比如要做一个页面计数器,可以使用单例模式,非常简单,直接看代码

//页面计数器
public class PageCounter {

    private static final PageCounter instance = new PageCounter();
    // 计数器
    private AtomicLong counter = new AtomicLong(0);

    private PageCounter() {

    }

    public static PageCounter getInstance() {
        return instance ;
    }

    public void add() {
        counter.getAndAdd(1);
    }

    public long get() {
        return counter.get();
    }

}

五、模式扩展

说到单例模式,很多人想到的是如何创建单例模式,有很多种创建方法,懒汉、恶汉、双重锁等等,此处大概介绍一下。

第一种(饿汉)

//饿汉模式(推荐),类加载时就创建了
//优点:1、线程安全;2、调用getInstance时速度快
//缺点:1、无法延迟加载;2、有可能浪费资源,无人调用getInstance()时,仍然创建了实例
public class Singleton01 {

    private static final Singleton01 instance = new Singleton01();

    private Singleton01() {
    }

    public static Singleton01 getInstance() {
        return instance ;
    }
}

第二种(饿汉变种)

//饿汉模式变种,类加载时就创建了,和上一个模式区别不大,只是能在static中加入逻辑处理
public class Singleton02 {

    private static Singleton02 instance = null;

    static {
        // 此处可以写一些逻辑
        instance = new Singleton02();
    }

    private Singleton02() {
    }

    public static Singleton02 getInstance() {
        return instance ;
    }
}

第三种(懒汉)

//懒汉(线程不安全),用到了再去初始化
//优点:延迟加载
//缺点:致命的并发问题,可能导致创建多次
public class Singleton03 {

    private static Singleton03 instance = null;

    private Singleton03() {
    }

    public static Singleton03 getInstance() {
        if ( instance == null ) {
            // 此处有并发问题
            instance = new Singleton03();
        }
        return instance ;
    }
}

第四种(懒汉变种)

//懒汉(线程安全)
//优点:延迟加载
//缺点:效率低下,初始化完毕后,getInstance()方法根本不需要同步了
public class Singleton04 {

    private static Singleton04 instance = null;

    private Singleton04() {
    }

    public synchronized static Singleton04 getInstance() {
        if ( instance == null ) {
            instance = new Singleton04();
        }
        return instance ;
    }
}

第五种(双重锁定检查)

//双重锁定检查
//优点:延迟加载,效率高
//缺点:jdk1.5之后才可以用
//由于jdk1.5之前编译器允许处理器乱序执行,所以可能导致获取到没初始化完毕的instance
public class Singleton05 {

    private static Singleton05 instance = null;

    private Singleton05() {
    }

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

第六种(枚举)

//枚举方式(推荐),Effective Java作者Joshua Bloch推荐的方式
//优点:不仅能避免线程同步问题,还能防止反序列化生成新的对象,相当严谨
//最主要的是非常的简单
//缺点:枚举是jdk1.5之后加入的特性,对版本有要求
public enum Singleton06 {
    instance;

    public void someMethod() {
        // 业务逻辑方法
    }
}

第七种(静态内部类)

//静态内部类
//优点:解决线程安全问题,而且可以延迟加载,基本上是曾经最好的办法
//缺点:代码复杂
public class Singleton07 {

    private static class RealSingleton {
        static final Singleton07 instance = new Singleton07();
    }

    public static Singleton07 getInstance() {
        return RealSingleton.instance;
    }

}

单例模式创建方法有很多种,没有最好的,只有最合适的,比如第七种方法比较好,但是没必要为了一个不会出现的问题而使用很复杂的第七种模式,如果没有需要延迟加载的地方(如读取配置文件等),推荐第一种模式,如果是JDK1.5以上,推荐使用枚举的方法。

单例模式还有个地方要注意,只有1个实例,虽然构造函数私有化,外边不能new了,但是还有其他方式创建对象实例,如反序列化时,可能得到另一个实例,此时就要考虑序列化对单例的影响,还有不同类加载器(ClassLoader)对单例的影响等都要考虑。

其实就是创建方式要支持的级别,这就需要根据实际情况,选择你的创建方式了:

1、每次从getInstance()都能返回一个且唯一的一个对象。

2、希望这个方法能适应多线程并发访问。

3、并发时方法性能尽可能高。

4、实现延迟加载(Lazy Load),在需要的时候才被构造,而且要能够处理业务逻辑。

5、能够处理多ClassLoader、多JVM,防止反序列化等情况。

设计模式学习01—单例模式

时间: 2024-07-29 11:07:39

设计模式学习01—单例模式的相关文章

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

原地址:http://blog.csdn.net/xu__cg/article/details/70182988 Java单例模式是一种常见且较为简单的设计模式.单例模式,顾名思义一个类仅能有一个实例,并且向整个系统提供这一个实例. 单例模式的特点: 单例类仅能有一个实例. 单例类必须为自己创建实例. 单例类必须向外界提供获取实例的方法. 以下是几种实现方法 一.懒汉式单例(能够延时加载) public class SingleTon { private static SingleTon ins

【我的设计模式学习】单例模式

单例模式大概是最直观的一种设计模式了.尽管直观却不简单. 数学与逻辑学中,singleton定义为"有且仅有一个元素的集合".单例模式可以如下定义:"一个类有且仅有一个实例,并且自行实例化向整个系统提供". 我比较喜欢Design Patterns 一书中的描述"保证一个类仅有一个实例,并提供一个访问它的全局访问点". 单例模式的特点 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例

C#设计模式学习笔记-单例模式

最近在学设计模式,学到创建型模式的时候,碰到单例模式(或叫单件模式),现在整理一下笔记. 在<Design Patterns:Elements of Resuable Object-Oriented Software>中的定义是:Ensure a class only has one instance,and provide a global point of access to.它的主要特点不是根据客户程序调用生成一个新的实例,而是控制某个类型的实例数量-唯一一个.(<设计模式-基于C

【C++深入浅出】设计模式学习之单例模式

但凡成为大家公认的模式,都是有一些不可小觑的威力,今天分享一个简单的设计模式:单例模式. 单例模式用于一些只希望有一个实例的类或者只希望执行一次的操作:校长只能有一个.老板只能有一个.用户点击弹窗只希望弹出一次.用全局变量等方式也可以实现,但是多了很多判断和处理代码,并且职责模糊,类的唯一实例化是交给调用者进行判断处理的,每调用一次就要做一次判断,重复了很多工作量,如果是多线程编程,不好的同步设计更是会导致程序卡顿. 如果还在为这些重复工作苦恼,是时候着手单例模式了:设计简单.调用方便.一劳永逸

设计模式学习系列——单例模式

单例模式 单例模式(Singleton Pattern),在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例.即一个类只有一个对象实例. 优点:1)在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如首页页面缓存). 2)避免对资源的多重占用(比如写文件操作). 缺点:违背了"单一职责原则",该类既是工厂又是产品(自己创建了自己): 从具体实现角度来说,就是以下三点: 一是单例模式的类只提供私有的构造函数, 二

Java设计模式学习记录-单例模式

前言 已经介绍和学习了两个创建型模式了,今天来学习一下另一个非常常见的创建型模式,单例模式. 单例模式也被称为单件模式(或单体模式),主要作用是控制某个类型的实例数量是一个,而且只有一个. 单例模式 单例模式的实现方式 实现单例模式的方式有很多种,大体上可以划分为如下两种. 外部方式 在使用某些全局对象时,做一些“try-Use”的工作.就是如果要使用的这个全局对象不存在,就自己创建一个,把它放到全局的位置上:如果本来就有,则直接拿来使用. 内部实现方式 类型自己控制正常实例的数量,无论客户程序

设计模式学习之单例模式

一.单例模式实现方式 常见的 1.饿汉方式 示例代码: package com.mt.singleton;/** * 设计模式单例模式之饿汉实现方式 * 线程安全,调用效率高,但无法实现延迟加载 * @author MT * */public class SingletonDemo1 { private static SingletonDemo1 s = new SingletonDemo1(); //私有化构造器 private SingletonDemo1(){   } //添加获取对象的公

设计模式学习 之 单例模式

单例模式: 定义 一个类有且仅有一个实例,并且自行实例化向整个系统提供. <来自百科> 这些 概念和要点 是 了解 单例模式的 一些基本概念 C# 单例实现要点 Singleton模式是限制而不是改进类的创建. Singleton类中的实例构造器可以设置为Protected以允许子类派生. Singleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Singleton模式的初衷违背. Singleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Si

php设计模式学习(单例模式和观察者模式)

1.单例模式 前几天跟一个朋友讨论的时候,朋友提了一个问题,为什么所有类不都用单例模式,都用单例模式真的是最好的吗? php中不管是new出来的对象还是静态变量,只对本次请求有效,当一次请求结束的时候,所有的对象和变量都会被销毁. 当一个对象每次请求只用一次的时候,单例模式和直接new没什么区别吧. 另一点,当一个对象涉及较多的属性修改或设置,又需要多次使用,很多时候可能我们更希望每次都进行初始化,而不是直接使用一个不知道被做了哪些操作的对象 综上,个人认为,单例模式更适用于一次请求需要多次使用