设计模式之——单例模式

单例模式属于创建模式的一种,在Java中也是最重要、最简单、最常用的设计模式之一,当遇到以下情况时,单例模式就派上用场了。

再比如——对象。。。。。。。。。。。

一般一个类能否做成单例,最容易区别的地方就在于,这些类,在应用中如果有两个或者两个以上的实例会引起错误,又或者我换句话说,就是这些类,在整个应用中,同一时刻,有且只能有一种状态。

单例模式的实现方法主要有四种:饿汉式、懒汉式、工厂方法、枚举方法。前两者是比较常见的的实现方法,当然如果面试时能将后两项也完美阐述的话,说明你的水平可不是一般货色可比的..>.<..下面贴代码,主要阐述都在注释里。。

一、饿汉式

package Singleton;
/*
 * 单例模式
 * 应用场合:有些对象只需一个就可以了
 * ①要求生成唯一序列号的环境
 * ②在整个项目中需要一个共享访问点或共享数据
 * ③需要定义大量的静态常量和静态方法(如工具类)的环境,可以使用单例
 * ④创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源 
 * 
 * 作用:保证整个应用程序中某个实例只有一个,并提供一个访问他的全局访问点
 * 类型:饿汉模式,懒汉模式
 * 
 * */
public class Singleton {
    //饿汉模式:缺点是类实例化后不能添加逻辑
    
    //1.将构造方法私有化,不允许外部直接创建对象
    private Singleton(){
        
    }
    //2.创建类的唯一实例         static 静态的可将其变为类的成员
   private static final Singleton instance=new Singleton();    
    
    //3.提供一个用于获取实例的方法
    public static Singleton getInstance(){
        return instance;
    }
    
}

二、懒汉式

package Singleton;
/*
 * 懒汉模式:只声明实例,并没有实例化
 * 但类加载时,并没有创建实例,而当用户获取时,开始做判断(是否为空),若为空,才创建实例
 * 创建之后(第2、3、4···)再来获取时,由于实例已存在,就不会再创建实例了
 * 
 * */


public class Singleton2 {
    //懒汉模式
    
   //1.将构造方法私有化,不允许外部直接创建对象
    private Singleton2(){
        
    }
    //2.声明类的唯一实例,使用private static 修饰
    private static Singleton2 instance;
    
    //3.提供一个用于获取实例的方法,使用public static修饰
      public static Singleton2 getInstance(){
          if(instance==null){    //多线程下不安全
              instance=new Singleton2();
          }
          return instance;
      }    
    
}

三、工厂化方法,可以在创建实例过程中添加逻辑(主要针对懒汉式的缺点)

package Singleton;
/**
 * 工厂化方法:可以在实例过程中添加逻辑
 * */
public class Singleton3 {

    private Singleton3(){
        
    }
    private static Singleton3 instance=new Singleton3();
    
    public static Singleton3 getInstance(){
        return instance;
    } 
    
}

四、枚举类方法(注意是 ~枚举 ~ 类   enum),最佳推荐,代码最简洁  **.**

package Singleton;
/**
 * 枚举类方式:最佳实践,推荐
 * effective java 推荐
 * */
public enum Singleton4 {
    instance;
}

 

五、对前四种单例方法验证一下,注意看实例创建的方式

package Singleton;

/*懒汉模式和饿汉模式的区别:
 * 懒汉模式:加载类时比较快,但运行时获取对象的速度比较慢,线程不安全
 * 饿汉模式:加载类时比较慢,但运行时获取对象的速度比较快(因对象早已创建好),线程安全
 * 
 * 
 * */



public class TestSingleton {
    //饿汉模式
    public static void main(String[] args) {
        System.out.println("饿汉模式");
        Singleton s1=Singleton.getInstance();
        Singleton s2=Singleton.getInstance();
        if(s1==s2){
            System.out.println("s1和s2是同一个实例");
        }else{
            System.out.println("s1和s2是同一个实例");
        }
        
        
        //懒汉模式
        System.out.println("懒汉模式");
        Singleton2 s3=Singleton2.getInstance();
        Singleton2 s4=Singleton2.getInstance();
        if(s3==s4){
            System.out.println("s3和s4是同一个实例");
        }else{
            System.out.println("s3和s4不是同一个实例");
        }
        
        
        //工厂方法
        System.out.println("工厂方法模式");
        Singleton3 s5=Singleton3.getInstance();
        Singleton3 s6=Singleton3.getInstance();
        if(s5==s6){
            System.out.println("s5和s6是同一个实例");
        }else{
            System.out.println("s5和s6不是同一个实例");
        }
     
        
        //枚举
        System.out.println("枚举");
        Singleton4 s7=Singleton4.instance;
        Singleton4 s8=Singleton4.instance;
        if(s7==s8){
            System.out.println("s7和s8是同一个实例");
        }else{
            System.out.println("s7和s8不是同一个实例");
        }
        
    }
}

运行结果:

 

六、针对最经典的懒汉模式易造成线程不安全的问题再多加一句废话

为什么说懒汉式是最经典的呢?因为饿汉模式这种方式最主要的缺点就是一旦我访问了Singleton的任何其他的静态域,就会造成实例的初始化,而事实是可能我们从始至终就没有使用这个实例,造成内存的浪费。

假设n个线程,当并发访问的时候,第一个调用getInstance方法的线程A,在判断完singleton是null的时候,线程A就进入了if块准备创造实例,但是同时另外一个线程B在线程A还未创造出实例之前,就又进行了singleton是否为null的判断,这时singleton依然为null,所以线程B也会进入if块去创造实例,这时问题就出来了,有两个线程都进入了if块去创造实例,结果就造成单例模式并非单例。

 

posted @ 2017-03-03 12:36  雪山上的蒲公英  阅读(356)  评论(0编辑  收藏  举报
/* 返回顶部代码 */