单例模式---设计模式(一)

单例模式:确保一个类最多只有一个实例,并提供一个全局访问点

单例模式类图:
这里写图片描述

类图说明:

首先说明一下该类图中的符号:
类:
使用三层矩形框表示
第一层:显示类的名称,如果是抽象类,则就用斜体显示
第二层:字段和属性
第三层:类的方法

“+”:表示public
“-”:表示private
“#”:表示protected

接口:
使用两层矩形框表示,与类图的区别主要是顶端有<<interface>>显示
第一层:接口的名称
第二层:接口的方法

继承类(extends):
用空心三角形+直线表示

实现接口(implements):
用空心三角形+虚线表示

关联(Association):
用实线箭头来表示

聚合(Aggregation):
用空心菱形+实线箭头来表示
聚合:表示一种弱的‘拥有’关系,体现的是A对象可以包含B,但B对象不是A对象的一部分,例如:公司和员工

组合(Composition):
用实心菱形+实线箭头来表示
组合:部分和整体的关系,并且生命周期是相同的。例如:人和手

依赖(Dependency):
用虚线箭头来表示
例如:动物和氧气

单例模式

Singleton类称为单例类,通过使用private的构造函数,确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。
单例模式的通用代码:
饿汉式:

package singletontest;

/**
 * 单例模式(饿汉模式)
 * 应用场合:有些对象只需要一个就足够了,如古代皇帝,老婆
 * 作用:保证整个应用程序中某个实例有且只有一个
 * 类型:懒汉模式,饿汉模式
 * @author 张耀晖
 *
 */
public class Singleton {
    //1.将构造方法私有化,不允许外部直接创建对象
    private Singleton(){

    }
    //2.创建类的唯一实例,使用private static修饰
    private static Singleton singleton = new Singleton();

    //3.提供一个用于获取实例的方法,使用public static修饰
    public static Singleton getInstance(){
        return singleton;
    }


}

懒汉式:

package singletontest;

/**
 * 懒汉模式
 * 区别:饿汉模式的特点是加载类时比较慢,但运行时获取对象的速度比较快,线程安全
 *     懒汉模式特点是加载类时比较快,但运行时获取对象速度比较慢,线程不安全
 * @author 张耀晖
 *
 */
public class Singleton2 {
    //1.将构造方法私有化,不允许外边直接创建对象
    private Singleton2(){

    }
    //2.声明类的唯一实例,使用private static修饰
    private static Singleton2 singleton2;
    //3.提供一个用于获取实例的方法,使用public static修饰
    public static Singleton2 getInstance(){
        if(singleton2==null){
            singleton2 = new Singleton2();
        }
        return singleton2;
    }

}
package singletontest;

public class Test {
    public static void main(String[] args) {
        //饿汉模式
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        if(s1==s2){
            System.out.println("s1和s2是同一个实例");
        }else{
            System.out.println("s1和s2不是同一个实例");
        }

        //懒汉模式
        Singleton2 s3 = Singleton2.getInstance();
        Singleton2 s4 = Singleton2.getInstance();
        if(s3==s4){
            System.out.println("s3和s4是同一个实例");
        }else{
            System.out.println("s3和s4不是同一个实例");
        }
    }

}

运行结果:
这里写图片描述

注意:
懒汉式是存在线程不安全问题的。
懒汉式在低并发的情况下有可能不会出现问题,但是当高并发的情况下则可能在内存中出现多个实例。
eg:如果一个线程A执行到singleton2 = new Singleton2(),但还没有获得对象(对象初始化是需要时间的),第二个线程B也在执行,执行到singleton2==null的判断,那么线程B获得判断条件也是为真,于是继续运行下去,线程A获得一个对象,线程B也获得一个对象,在内存中就出现了两个对象。

解决懒汉式的线程不安全问题的方法:
在getInstance()方法前加synchronized关键字。不过这样的话只适应调用实例对象少的情况,如果调用实例对象比较频繁synchronized就会影响系统的执行效率。那么就建议使用饿汉式。
饿汉式也有一定的问题,在不使用类的实例对象的时候也会创建该类的实例对象,造成一定的资源浪费。

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

  2. 单例模式的缺点
    •单例模式没有接口,扩展很困难,若要扩展,除了修改代码没有第二种途径可以实现。单例模式为什么不能增加接口呢?因为接口对单例模式是没有任何的意义,它要求“自行实例化”,并且提供单一实例、接口或抽象类是不可能被实例化的。
    •单例模式对测试是不利的。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
    •单例模式与单一职责原则有冲突。一个类应该只实现一个的逻辑,而不关心它是否是单例的,决定它是不是要单例是环境决定的,单例模式把“要单例”和业务逻辑融合也在一个类中。

  3. 单例模式的使用场景

    在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反应”时,则可以采用单例模式,具体的场景如下:
    •要求生成唯一序列号的环境;
    •在整个项目中需要有访问一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
    •创建一个对象需要消耗的资源过多,如要访问IO、访问数据库等资源;
    •需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式);

单例模式的扩展

现在有一个问题:如果要求一个类只能产生固定数量(大于1个)的对象怎么办?

package com.test.singletonTest;

import java.util.ArrayList;
import java.util.Random;

/**
 * 单例模式的扩展,产生固定数量的(大于1个)的实例对象
 * 2015年9月17日 下午5:08:15
 * @author 张耀晖
 *
 */
public class SingletonTest {

    private static int maxnum = 3;//定义最多能产生的实例对象的数量
    private static ArrayList<SingletonTest> singletonList = new ArrayList<SingletonTest>();//定义一个集合,容纳所有的实例对象
    private static ArrayList<String> singletonNameList = new ArrayList<String>();//定义一个集合,容纳所有对象的私有属性

    //当前实例对象的标号
    private static int countOfNum;

    //产生所有的对象
    static{
        for (int i = 0; i < maxnum; i++) {
            singletonList.add(new SingletonTest("实例对象:"+i+1));
        }
    }

//  //将类中默认的无参构造方法重写为private,防止在外部创建该类的实例对象
//  private SingletonTest(){
//      
//  }

    //传入实例对象的名称,创建实例对象
    private SingletonTest(String name){
        singletonNameList.add(name);
    }

    //随机获得一个实例对象
    public static SingletonTest getInstance(){
        Random random = new Random();
        countOfNum = random.nextInt(maxnum);
        //随机取出一个实例对象
        SingletonTest singletonTest = singletonList.get(countOfNum);
        return singletonTest;
    }

    //获取实例对象的私有属性
    public static String getSingletonName(){
        String name = singletonNameList.get(countOfNum);
        return name;
    }

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值