单例模式
为什么引入单例模式?
当我们需要查看系统的进程,内存的使用情况时,打开任务管理器,可以查看到系统的当前的运行状态,但是在没有关闭上一个任务管理器的时,我们在打开一个任务管理器时,这个时候不会出现第二个任务管理器,因为任务管理器是获取系统的运行情况,实时的分析系统,只需要一个就够了,如果打开多个,有可能造成系统的分析情况不一致。
在这种类似的情况,我们只需要一个对象就可以满足我们的需求,或者说因为某些情况,我们只能,必须只有一个实体对象,在这种需求之下,单例模式被需要。
单例模式是什么?
确定某个类的实例只有一个,而且自行实例化并向整个系统提供这个实例。
即不重复创建实例对象,任何人使用同一个对象。
单例模式核心思想:
私有化构造器,使得外部不能直接通过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());
}
}
}