黑马程序员——设计模式1:单例设计模式

------- android培训java培训、期待与您交流! ----------

1. 什么是设计模式

设计模式的概念最早是从建筑设计领域起源的,人们在长期的建筑施工过程当中总结出了很多经验教训,这些经验教训在被后人反复应用的过程当中就慢慢成为了一些约定俗成的规律,或者说某种通用的解决方法,就将这些规律或者方法统称为设计模式*

后来这个概念延伸到软件开发行业,也称为设计模式,就是那些在软件开发过程中解决问题最为行之有效的方法。现在常用的设计模式有23种。

*:摘自毕老师视频。

2. 类比理解设计模式

我们举一个生活中的例子来进一步理解什么是设计模式。比如说吃饭,为了使得吃饭更为方便,咱们的祖先经过长期的实践总结发现,用两根木棍儿夹取食物是比较方便的,后来就有了筷子。这就是一种解决问题的最为行之有效的方法。当然筷子本身是表现形式(也就是代码),而原理(思想)其实就是两根棍儿,无论什么材质,只要能夹取食物就可以。当然,每个国家的人们的习惯不同,比如西方国家就比较喜欢用刀叉,这也是一种解决问题的方法;再比如印度或者非洲的一些部落,那里的人们吃东西喜欢直接用手抓着吃,这也是个办法。总之,只要能解决问题,并能够给被他人所认可,进而成为一种广泛的习惯,就可以称之为设计模式。

3. 设计模式的优点

优点1:在软件开发过程中,这些解决问题的方法是前人反复验证总结出来的,遇到问题不必再费脑筋去思考,直接套用就可以了。

优点2:设计模式强调的是解决问题的思想,而不是具体的代码,因此,掌握了这种思想,可以应用到各种编程语言中,不仅仅是Java。

设计模式虽然很好用,但是它的易用性只有在与实际开发结合时才能体现,否则单纯谈论他的思想就会比较空泛。

4. 单例设计模式的由来

设计模式有很多,我们今天先来学习单例设计模式。单例设计模式解决的是如何使一个类在内存中只能存在一个对象的问题。有朋友要问了,一个类本身就可以创建多个对象,为什么要限制对象的个数只有一个呢?我们举一个游戏中的例子。比如很多朋友都玩儿过魔兽争霸3,每局游戏的开始通常都需要5个农民去采集金矿,实际上可以理解为5个农民对象去访问一个金矿对象。在这个局部区域(一个基地)内这个金矿对这五个农民来说是唯一的存在,不能说5个农民想访问金矿,就要创建5个金矿,这是不符合现实情况的。

5. 单例设计模式的实现方法一

上面啰嗦了这么多,现在我们来讲一下如何具体实现单例设计模式。首先要理清思路,解决方法和代码自然就有了。为了叙述方便将单例对象称为S。

1) 思路

第一步:既然我们要保证S对象的唯一性,就要禁止其他类可以随意通过“new”关键字创建新的S对象。

第二步:保证唯一并不说一个都没有,为了让其他对象可以访问到S对象,只好在本类内部创建一个自定义S对象。

第三步:不仅要在本类内部创建自定义S对象,还要对外提供一些访问S对象的方法。

2) 实现方法

与上述思想相对应的具体实现方法:

第一步:将构造函数私有化,禁止通过“new”关键字创建新的对象。

第二步:在类中创建一个本类对象。

第三步:提供一个方法可以获取到这个对象。

3) 代码体现

代码1:

class Single
{
<span style="white-space:pre">	</span>//私有化构造函数
<span style="white-space:pre">	</span>private Single(){}
 
<span style="white-space:pre">	</span>/*
<span style="white-space:pre">		</span>创建一个本类对象
<span style="white-space:pre">		</span>由于静态成员只能访问静态成员,所以本类对象也要被定义为静态
<span style="white-space:pre">	</span>*/
<span style="white-space:pre">	</span>private static Single s = new Sinlge();
 
<span style="white-space:pre">	</span>/*
<span style="white-space:pre">		</span>对外提供一个可以访问到这个对象的方法
<span style="white-space:pre">		</span>由于不能创建对象,所以只能将该方法定义为静态,通过类名调用
<span style="white-space:pre">		</span>返回值类型为本类类型
<span style="white-space:pre">	</span>*/
<span style="white-space:pre">	</span>public static Singel getInstance()
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>return s;
<span style="white-space:pre">	</span>}
}
class SingleDemo
{
<span style="white-space:pre">	</span>public static void main(String[] args)
<span style="white-space:pre">	</span>{
<span style="white-space:pre">		</span>//用类名调用静态方法获取对象
<span style="white-space:pre">		</span>Single s = Single.getInstance();
<span style="white-space:pre">	</span>}
}
上述代码在内存中的执行过程如下:首先加载SingleDemo类,在方法区中为该类开辟一块空间,并将main方法存储在这片空间。然后由虚拟机调用main方法,在栈内存为main方法开辟一块空间,先从赋值语句的右边开始执行,加载Single类,并在方法区中为Single类开辟一块空间,同时对静态成员变量s进行默认初始化,并且将该类的所有方法都存储于此(静态成员和非静态成员分开存储,包括构造方法)。随后为静态成员变量s进行显示初始化,在堆内存为Single对象开辟一块空间,然后将Single对象的地址值赋给静态成员变量s,s就指向了堆内存的Single对象,至此Single类加载完毕。通过类名调用getInstance方法返回Single对象,也就把Single对象在堆内存中的地址值赋给了栈内存中Single类类型变量s,栈内存中的s变量同样也指向了堆内存中的Single对象。这样一来,即使多次使用getInstance方法获取了Single对象,实际上这些对象都是同一个。

 

小知识点1:

虽然没有显示地使用static关键字,构造器实际上也是静态方法。摘自《Java编程思想》。

 

4) 验证

为了验证上述代码的可行性,我们运行一下下述代码,

代码2:

class Single
{
	private int num;
	private static Single s = new Single();
 
	private Single(){}
 
	public int getNum()
	{
		return num;
	}
	public void setNum(int num)
	{
		this.num = num;
	}
 
	public static Single getInstance()
	{
		return s;
	}
}
 
class SingleDemo2
{
	public static void main(String[] args)
	{
		Single s1 = Single.getInstance();
		Single s2 = Single.getInstance();
 
		System.out.println(s1.getNum());
		s2.setNum(30);
		System.out.println(s1.getNum());
       }
}
上述代码的运行结果为:

0

30

从上面的结果来看,s1和s2确实都指向了同一个对象。

 

需要强调的一点是,单例设计模式它首先是一种思想,在实际开发过程中,一定要和实际需求相结合,仅仅写出代码1是什么作用都没有的。换句话说,该如何描述一个事物就如何描述一个事物,如果想要保证这个事物的唯一性,就在定义该类的时候加上代码1即可。

6. 实现单例设计模式的方式二

1) 饿汉式与懒汉式

上面所讲的是最常用的单例实现方式,一般称为饿汉式。饿汉式的特点是:本类对象随着类的加载而创建;而我们下面要讲的实现方式称为懒汉式,它的特点是:本类对象只有在getInstance方法被调用时才被创建,这种形式称为对象的延时加载。先看代码,

代码3:

class Single
{
	//这里的成员变量赋值为空
	private static Single s = null;
	private Single(){}
 
	public static Single getInstance()
	{
		//只有当getInstance方法被调用,并且变量s指向空的时候,才创建Single对象
		if( s== null)
			s = new Single();
			return s;
	}
}
class SingleDemo3
{
	public static void main(String[] args)
	{
		Single s = Single.getInstance();
	}
}
针对上述代码我们来说说懒汉式和饿汉式的区别。当加载Single类的时候,在方法区为该类开辟一块空间,并为静态成员变量s进行默认初始化。之后如果是饿汉式,就会直接对其进行显示初始化,在堆内存中创建Single对象,并将地址值赋给s。而我们从代码3可以看出,懒汉式的显示初始化值是null,实际上等于没有进行初始化。接着,调用getInstance方法,这时候通过if语句判断,才在堆内存中创建Single对象,并将其地址值赋给静态变量s,这一过程就是所谓的延时加载。

从上面的描述来看,好像懒汉式相对饿汉式更为优化,因此只有在需要的时候才创建对象。但是实际上,在实际开发过程中饿汉式才是更为常用的选择。因为,饿汉式不仅代码相对简单,而且更为安全。

2) 懒汉式的安全问题

关于安全问题,我们先讲讲计算机是如何同时处理多个任务的。计算机在运行多个程序的过程中,总给人一种在同时处理多个任务的错觉,实际上,单个CPU在同一时间只能处理一个任务,或者说运行一个程序,之所以产生同时运行的错觉,是因为CPU在多个任务之间进行快速切换,切换速度太快,人是感觉不到的,所以会产生上述错觉。

说完了计算机处理多任务的方式以后,我们接着来说懒汉式的安全问题。我们假设下述情况。比方有A和B两个程序来个执行上述代码3,当A执行到代码中if判断语句以后,CPU切换到了程序B。程序B一步步执行也执行到了if判断语句,CPU再次切换到了程序A,程序A继续下面的代码创建Single对象,并返回该对象,结束。然后CPU再切换到程序B,同样也继续往下执行,又创建了一个Single对象,返回对象,结束。此时,内存中就存在了两个Single对象了,无法再保证Single的唯一性了。

3) 懒汉式安全问题的解决方法

解决懒汉式安全问题的方法有两种:

方法一:

在getInstance方法前增加一个修饰符“synchronized”,意思是同步,它的作用是:相当于一个锁,当有某个程序在运行getInstance代码时,别的程序就无法执行,就像上了锁一样。这样一来即使程序A运行到一半停止,程序B也不可能执行代码,因为A程序还没有执行完毕,等A执行完,变量s已经指向了Single对象,B程序经if判断也就不再创建新对象了。这个方法虽然解决了问题,但是,因为每次执行getInstance方法的代码时都要进行“synchronized”判断,程序运行比较低效。

方法二:

既高效又能解决问题的方案如下,

代码4:

class Single
{
	private static Single s = null;
	private Single(){}
 
	public static Single getInstace()
	{
		if(s == null)
		{
			synchronized(Single.class)
			{
				if(s == null)
					s = new Single();
			}
		}
		return s;
	}
}
class SingleDemo4
{
	public static void main(String[] args)
	{
		Single s = Single.getInstance();
	}
}
该方法通过双重判断的方式,减少了判断锁的次数,从而提高了效率,大家也可以用上述程序A和B的情况验证一下,是否能解决问题,同样synchronized代码块也相当于一个锁。关于单例设计模式由于涉及多线程的知识,将在后面多线程部分详细说明。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值