1.单例模式(单例指一个实例,即一个类只能创建一个对象)
需求:在一个软件中,用户更改字体设置,保存后,下次运行该软件,字体是上次修改的字体,这说明用户在本软件中更改某项设置,只产生一个属性面板对象,这就需要单例模式。思路:要使一个类只能创建一个对象,需要做到:
(1)不能在外部创建对象
(2)在单例类中创建好一个对象,供外部使用
(3)提供一个功能使外部可以调用这个创建好的对象
步骤:
(1)把构造函数私有化
(2)在类内部创建一个对象作为类的成员 (把对象作为成员变量)
(3)创建一个方法返回这个创建好的对象成员
(4)现在不能通过new创建对象了,要访问类中的方法,就要把方法设成静态的,通过类名直接调用,这时那个对象成员也要设成静态的,才能在方法中访问。
饿汉式:(不管是否调用类中方法都创建一个对象)
class SingleDemo{
private static Single s=new SingleDemo(); //对象作为类的成员变量
private SingleDemo(){}; //把构造函数隐藏,不能再外部new多个对象
public static SingleDemo getInstance(){
return s;
}
}
懒汉式:(什么时候用到这个对象,再创建对象)
class SingleDemo{
private static Single s=null; //先保留空间,并不创建对象
private SingleDemo(){};
public static SingleDemo getInstance(){
if(s==null){
s= new SingleDemo(); //什么时候调用方法才创建对象
}
return s;
}
}
在其他类中应用:SingleDemo s1=SingleDemo.getInstance();
如果是饿汉式,执行到上述语句时,虚拟机加载SingleDemo后,就加载静态成员,这时类中已经有对象创建好了,其引用放在方法区,然后执行getInstance方法,返回s1对象
如果是懒汉式,执行上述语句时,虚拟机加载SingleDemo类后,加载静态成员,但这时类中还没有创建对象,执行getInstance方法时才开始创建对象,再返回,相当于对象的延时创建。
貌似两种方法都可行,但分析一下就会发现,懒汉式肯能存在问题?
因为cpu是在进程之间切换,每个进程可能还有多线程,当很多用户同时访问该类时,可能出现以下情况:
public static SingleDemo getInstance(){
if(s==null){
--->A线程进行到这时,cpu切换到别的线程了
--->某个时刻,B也进来了,因为A线程并没有创建对象
s= new SingleDemo();
}
return s;
}
这样就可能创建两个对象。解决方法,用同步 Synchronization给线程加锁。
public static Synchronization SingleDemo getInstance(){
if(s==null){
s= new SingleDemo();
}
return s;
}
这样每次只能进来一个线程,问题是解决了,但是每次调用这个方法都会判断锁,效率降低了,还有好的解决方法吗?可以不在方法上加锁,缩小加锁的范围,当s==null时,放线程进来,这时加锁创建对象,即
public static SingleDemo getInstance(){
if(s==null){
Synchronization (Single.class){
s= new SingleDemo();
}
}
return s;
}
这时虽然加锁了,还有可能出现几个线程同时潴留在锁里面
public static SingleDemo getInstance(){
if(s==null){
Synchronization (Single.class){
--->A线程进行到这时,cpu切换到别的线程了
--->某个时刻,B也进来了,因为A线程并没有创建对象
s= new SingleDemo();
}
}
return s;
}
这里就应该加个判断,是否创建对像
public static SingleDemo getInstance(){
if(s==null){
Synchronization (Single.class){
if(s==null){
s= new SingleDemo(); //只要进来一个就会创建对象,即使if外面还有B也会被挡在外面了
}
}
}
return s;
}
对懒汉式的解决需要双重判断,综上所述,懒汉式起初的偷懒,造成后面繁琐的弥补,所以通常采用饿汉式。
———————————————————————————————
模板方法模式
1. 需求:计算一个程序运行的时间
思路:分别取得程序运行前和运行后的时间,然后相见。
public class GetTime{
public void getTime(){
long start=System.currentTimeMillis();
for(int i=0;i<1000;i++){
System.out.print(i);
}
long end=System.currentTimeMillis();
System.out.println(“时间:”+(end-start)+"毫秒");
}
}
public class GetTimeDemo{
public static void main(String [] args){
GetTime gt=new GetTime();
gt.getTime();
}
}
GetTime类中的代码块是不确定的,可由用户控制,如果只因改变代码块而重新定义一个类继承GetTime类,重写getTime方法,这样代码会出现冗余,因为有些功能是固定的。所以可以考虑将不确定的代码块封装起来,
public class GetTime{
public void getTime(){
long start=System.currentTimeMillis();
runcode();
long end=System.currentTimeMillis();
System.out.println(“时间:”+(end-start)+"毫秒");
}
public void runcode(){
for(int i=0;i<1000;i++){
System.out.print(i);
}
}
}
这时,在其他类中继承GetTime类,重写runcode方法,创建GetTime对象,调用getTime方法即可。这时GetTime类中有默认的运行程序实例,如果不需要,可以将runcode抽象。还可以将getTime修饰为final,防止被子类重写,通用模式如下:
public class GetTime{
public void getTime(){
long start=System.currentTimeMillis();
runcode();
long end=System.currentTimeMillis();
System.out.println(“时间:”+(end-start)+"毫秒");
}
public abstract void runcode();
}
-----------------------------------------------------------------------------------
完整例子:
//什么是模板方法设计模式?
//当定义功能时,功能的一部分是确定的,另一部分是不确定的,而确定的部分又要用到不确定的
//部分,这时就可以把不确定的部分封装起来暴露出去,让子类去重写。
abstract class GetTime
{
public final void getTime() //定义为final,使子类不能重写该功能
{
long start=System.currentTimeMillis();
runCode();
long end=System.currentTimeMillis();
System.out.println("毫秒:"+(end-start));
}
abstract void runCode(); //需要运行的代码,如果有默认的,可以不用定义为抽象的
}
class SubTime extends GetTime
{
public void runCode() //子类只重写runCode方法
{
for(int x=0;x<1000;x++)
{
System.out.print(x+"\t");
}
}
}
public class TemplateDemo
{
public static void main(String []args)
{
SubTime st=new SubTime();
st.getTime();
}
}