单例模式

为什么引入单例模式?

当我们需要查看系统的进程,内存的使用情况时,打开任务管理器,可以查看到系统的当前的运行状态,但是在没有关闭上一个任务管理器的时,我们在打开一个任务管理器时,这个时候不会出现第二个任务管理器,因为任务管理器是获取系统的运行情况,实时的分析系统,只需要一个就够了,如果打开多个,有可能造成系统的分析情况不一致。

在这种类似的情况,我们只需要一个对象就可以满足我们的需求,或者说因为某些情况,我们只能,必须只有一个实体对象,在这种需求之下,单例模式被需要。

单例模式是什么?

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

即不重复创建实例对象,任何人使用同一个对象。

单例模式核心思想:

私有化构造器,使得外部不能直接通过new关键字创建对象,通过在类内部私有化的创建对象,向外提供一个公共的方法,使得外部通过这个方法获取同一个对象。

具体案例?

世界上没有相同的叶子,以叶子为例,叶子是独一无二的叶子,就像我们自己,都是独无一二的

UML:

代码:

package com.dong.DesignPattern;
/**
 *单例模式,私有化构造器 
 * 
 * @author liuD
 */
public class Leaves {
	private String name = "七叶草";
	private String color = "彩色";
	//私有化构造器
	private Leaves() {}
	//static修饰,⑴因为私有化构造器,所以只能通过公有的getInstance()方法获取对象,正式因为私有化,所以只能通过类名访问该方法,所以变量也只能是static
	//⑵static关键字修饰,属于静态类变量,随类的加载而加载,所有类共享这个变量。
	public static Leaves leaves  = null;
	
	public static Leaves getInstance() {
		//判断是否是第一次创建对象,是的话就创建,不是的话就返回原有的对象
		if( leaves  ==  null)
			leaves = new Leaves();
		return leaves;
	}
	//可以通过get方法获取对象,没有set方法,反之改变对象属性
	public String getName() {
		return name;
	}
	public String getColor() {
		return color;
	}
	@Override
	public String toString() {
		return "Leaves [name=" + name + ", color=" + color + "]";
	}
	
}

测试:

public class SingleMain {
	public static class GetObject implements Runnable{
		public void run() {
			Leaves le = Leaves.getInstance();
			System.out.println(Thread.currentThread().getName()+" 获取的对象为" + le.toString());
		}
	}
	public static void main(String[] args) {
		GetObject obj =new GetObject();
		for(int i =0 ;i<1000;i++) {
			Thread thread = new Thread(obj);
			thread.start();
		}
	}
}

优点:

任何时候,只能创建一个对象,对象是相同的,即在堆中的地址。可以防止重复创建对象,减少系统开销,

缺点:

没有接口,很难扩展,测试麻烦。

使用场景

要求一个类有且仅有一个对象;

如果这个对象的创建十分消耗资源,则可以使用单例模式,节省资源。

改进

上面的实现在串行中实现时没有问题的,但是在高并发的程序中,会出现创建多个对象。当线程A执行到leaves = new Leaves();的时候,但是构建对象需要时间,所以这个时候对象在这一瞬间还是null,如果线程B刚好也在执行,则执行到(leave == null)时,判断为真,他也会进入到创建对象的语句中,这样就创建了两个对象,所以使用synchronized关键字,进行同步操作,方法改进如下:

	public static Leaves getInstance() {
		//双重检测
		//判断是否是第一次创建对象,是的话就创建,不是的话就返回原有的对象
		if( leaves  ==  null)
			synchronized (Leaves.class) {	
				if(leaves == null) {
					leaves = new Leaves();
				}
			}
		return leaves;
	}

上面的例子都是懒汉式实现方法,当需要对象的时候才去创建,而不是在初始化类的时候就去创建对象,在初始化类的时候就去创建对象的方式是饿汉式的单例模式:

饿汉式单例模式:

public class Leaves {
	private String name = "七叶草";
	private String color = "彩色";
	
	//私有化构造器
	private Leaves() {}
	private final static Leaves leaves = new Leaves();
	
	public static Leaves getInstance() {
		return leaves;
	}
	@Override
	public String toString() {
		return "Leaves [name=" + name + ", color=" + color + "]";
	}
	

 扩展:创建固定实例数量的模式:

package com.dong.DesignPattern;

import java.util.ArrayList;
import java.util.Random;
/**
 * 创建固定数量(3)实例.
 * 
 * 运行结果:
 *  com.dong.DesignPattern.ThreeLeaves@383534aa
	com.dong.DesignPattern.ThreeLeaves@6bc168e5
	com.dong.DesignPattern.ThreeLeaves@383534aa
	com.dong.DesignPattern.ThreeLeaves@7b3300e5
	com.dong.DesignPattern.ThreeLeaves@7b3300e5

 * @author liuD
 *
 */
public class ThreeLeaves {
	private String name;
	private String color;
	
	private ThreeLeaves() {}
	
	private ThreeLeaves(String name,String color) {
		this.color = color;
		this.name = name;
	}
	//对象的最大数量
	private static int maxNumberThreeLeaves = 3;
	//容纳对象的列表
	private static ArrayList<ThreeLeaves> ObjectList = new ArrayList<ThreeLeaves>();
	//当前对象的序号
	private static int countNumOfThreeLeaves = 0;
	//创建对象
	static {
		for(int i = 0 ;i< 3;i++) {
			ObjectList.add(new ThreeLeaves(i+"",i+1+""));
		}
	}
	
	public static ThreeLeaves getInstance() {
		Random random =new Random();
		countNumOfThreeLeaves = random.nextInt(maxNumberThreeLeaves);
		return ObjectList.get(countNumOfThreeLeaves);
	}
	
	public static void main(String[] args) {
		for(int i = 0 ;i< 5;i++) {
			ThreeLeaves tl = ThreeLeaves.getInstance();
			System.out.println(tl.toString());
		}
	}
}