毕25-第一天(day06)-面向对象(设计模式之单例设计模式)

  • 单例设计模式

设计模式:解决某一问题最行之有效的方法。Java中有23种设计模式。

单例设计模式:解决一个类在内存中只存在一个对象。

想要保证对象唯一,有如下要求:

1、为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象;

2、还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对象;

3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

这三个要求的代码实现:

1、将构造函数私有化;

2、在类中创建一个本类对象;

3、提供一个方法可以获取到该对象。

代码:

class Single{
	private Single() {}		//构造函数私有化
	private static Single s = new Single();		//在本类中创建一个本类对象
	//提供一个方法获取该对象,由于在其它类中不能创建对象,不能调用获取getInstance方法,所以需要将该方法用static修饰,从而使用类名调用方法,同时上面的Single对象也要用static修饰(静态方法只能使用静态成员)
	public static Single getInstance() {	
		return s;
	}
}
class SingleTest{
	public static void main(String[] args) {
		Single ss = Single.getInstance();
	}
}

内存分析如下图:

首先进入main方法,将main方法压入栈,并加载Single类,将静态成员、静态方法以及方法放入方法区;

加载Single类时,新建了一个类对象Single,假设地址为0x0045,并且静态成员s指向该对象;

在main方法中,调用getInstance方法,获得了类中建立的s指向的对象,将ss也指向该对象;

这时假设再次调用getInstance方法,获得了类中建立的s指向的对象,将s1也指向该对象;

因此ss与s1指向的是同一对象,这就实现了一个类在内存中只存在一个对象。

对于事物该怎么描述还是怎么描述,当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。实例如下:

class Single{
	private int num;
	public void setNum(int num) {
		this.num = num;
	}
	public int getNum() {
		return num;
	}
	private Single() {}
	private static Single s = new Single();
	public static Single getInstance() {	
		return s;
	}
}
class SingleTest{
	public static void main(String[] args) {
		Single s1 = Single.getInstance();
		Single s2 = Single.getInstance();
		s1.setNum(23);
		System.out.println(s2.getNum());       //得到的结果为23,因为s1和s2指向的是同一个对象
	}
}
  • 单例设计模式方式二

单例设计模式有两种:饿汉式和懒汉式。

//这个是先初始化对象,称为:饿汉式
/*class Single{
	private Single() {}
	private static Single s = new Single();
	public static Single getInstance() {	
		return s;
	}
}*/

//这个是方法被调用时,对象才被初始化,也叫做对象的延时加载,称为:懒汉式。
//Single类进入内存后,对象还没有存在,只有调用了getInstance方法时,才建立对象。
class Single{
	private Single() {}
	private static Single s = null;
	public static Single getInstance() {
	    if(s==null)
                s = new Single();
            return s;
	}
}

class SingleTest{
	public static void main(String[] args) {
		Single s1 = Single.getInstance();
	}
}

但是上面的懒汉式会出现不安全的问题,因为如果是单核CPU,并行则不是真正意义上的并行,这时如果调用了三次getInstance方法,假设为A,B,当A运行到if(s==null)之后突然停下来了,则当前s仍为null,这时又来了一个B,运行到if(s==null)之后也突然停下来了,则当前s仍为null,这是A恢复过来,创建了一个对象,然后B又恢复过来,也创建了一个对象,则此时一个类中建立了两个对象,不具有了唯一性,就不是单例设计模式了。

对于这种不安全的问题,可以利用同步关键字来实现互锁,解决方法如下:

public static synchronized Single getInstance() {
    if(s==null)
        s = new Single();
    return s;
}

这是同时来了A,B,A进入方法之后,便上了锁,之后B来了判断锁住了,便不能进入该方法,但是上述方法每次都要判断是否为上锁状态,所以运行较慢,优化运行时间可以采用一下方式(双判断):

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

采用该改进之后,假设来了A,B,C,这是时A进入了方法,判断s==null,且未锁,则进入互锁的内部,这时卡住了的话,这时来了B,判断s==null,且已锁,则等待;等A结束且建立了一个对象,此时s!=null,这时C进入方法直接判断s是否为null,条件不满足,直接返回已创建好的Single对象,不会再创建新的Single对象。

相比于饿汉式,这个方法的代码比较多,因此定义单例设计模式,建议使用饿汉式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值