(6)单例模式和模板方法模式

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();
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值