java23种设计模式之单例模式

来源:互联网 发布:人机界面编程难不难 编辑:程序博客网 时间:2024/05/21 14:06

单例的特点:

    1.单例类只能有一个实例。

    2.单例类必须自己创建自己的唯一实例。

    3.单例类必须给所有其他对象提供这一实例。

应用场景:

    1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。
    2.控制资源的情况下,方便资源之间的互相通信。
示例:
    1. 计算机系统
    2.线程池
    3.缓存
    4.日志对象
    5.对话框
    6.显卡的驱动程序

创建的步骤:
    1.把构造方法私有化
    2.在类中提供一个本类的对象
    3.提供一个公共的方法,返回这个对象

分类:
    1.懒汉式
        1.1线程安全
        1.2非线程安全
    2.饿汉式
        2.1 普通
        2.2静态代码块
    3.静态内部类
    4. 枚举
    5.Double CheckLock(DCL)双重校验锁
    6.容器式

代码实现:

/*懒汉式单例设计模式

 *   

 * 1) 把构造方法私有化

 * 2) 在类中提供一个本类的对象

 * 3) 提供一个公共的方法并判断,返回这个对象

 */

public class Singleton {

/*1)显示的定义了一个构造方法, 系统只有这一个私有的构造方法*/

private Singleton() {

}

/*2)提供一个私有的静态的本类的对象, 懒汉式在定义对象之后 不进行初始化,在第一次使用时再初始化*/

private static Singleton obj;

 /*3) 提供一个公共的静态的方法返回本类的对象

注意:增加synchronized关键字,该方法为同步方法,保证多线程单例对象唯一*/

 

public static synchronized Singleton getInstance() {

if ( obj == null ) {

obj = new Singleton();

}

return obj;

}

}

 

/*懒汉式单例设计模式

 *   

 * 1) 把构造方法私有化

 * 2) 在类中提供一个本类的对象

 * 3) 提供一个公共的方法并判断,返回这个对象

 */

public class Singleton {

//1)显示的定义了一个构造方法, 系统只有这一个私有的构造方法

private Singleton() {

}

//2)提供一个私有的静态的本类的对象, 懒汉式在定义对象之后 不进行初始化,在第一次使用时再初始化

private static Singleton obj;

 //3) 提供一个公共的静态的方法返回本类的对象

public static Singleton getInstance() {

if ( obj == null ) {

obj = new Singleton();

}

return obj;

}

} 

 

 

/*饿汉式单例设计模式

 *

 * 1) 把构造方法私有化

 * 2) 在类中提供一个本类的对象

 * 3) 提供一个公共的方法,返回这个对象

 */

public class Singleton {

//1)显示的定义了一个构造方法, 系统只有这一个私有的构造方法

private Singleton() {

}

//2)提供一个私有的静态的本类的对象

private static Singleton obj = new Singleton();

 //3) 提供一个公共的静态的方法返回本类的对象

public static Singleton getInstance() {

return obj;

}

}

 

public class Singleton {  

      private Singleton instance = null;  

      static {  

      instance = new Singleton();  

      }  

      private Singleton (){}

      public static Singleton getInstance() {  

      return this.instance;  

      }  

 }  

 

/**

 * 静态内部类实现单例模式

 *

 */

public class StaticClassSingleton {

    //私有的构造方法,防止new

    private StaticClassSingleton() {

 

    }

 

    private static StaticClassSingleton getInstance() {

        return StaticClassSingletonHolder.instance;

    }

 

    /**

     * 静态内部类

     */

    private static class StaticClassSingletonHolder {

        //第一次加载内部类的时候,实例化单例对象

        private static final StaticClassSingleton instance = new StaticClassSingleton();

    }

}

第一次加载StaticClassSingleton类时,并不会实例化instance,只有第一次调用getInstance方法时,Java虚拟机才会去加载StaticClassSingletonHolder类,继而实例化instance,这样延时实例化instance,节省了内存,并且也是线程安全的。这是推荐使用的一种单例模式。

 

 

/**

 * 枚举单例模式

 *

 */

public enum EnumSingleton {

    //枚举实例的创建是线程安全的,任何情况下都是单例(包括反序列化)

    INSTANCE;

 

    public void doSomething(){

 

    }

}

枚举不仅有字段还能有自己的方法,并且枚举实例创建是线程安全的,就算反序列化时,也不会创建新的实例。除了枚举模式以外,其他实现方式,在反序列化时都会创建新的对象。

为了防止对象在反序列化时创建新的对象,需要加上如下方法:

    private Object readResole() throws ObjectStreamException {

        return instance;

    }

这是一个钩子函数,在反序列化创建对象时会调用它,我们直接返回instance就是说,不要按照默认那样去创建新的对象,而是直接将instance返回。

 

/*

Double CheckLock(DCL)模式

*/

public class DCLSingleton {

    //增加volatile关键字,确保实例化instance时,编译成汇编指令的执行顺序

    private volatile static DCLSingleton instance;

 

    private DCLSingleton() {

 

    }

 

    public static DCLSingleton getInstance() {

        if (instance == null) {

            synchronized (DCLSingleton.class) {

                //当第一次调用getInstance方法时,即instance为空时,同步操作,保证多线程实例唯一

                //当以后调用getInstance方法时,即instance不为空时,不进入同步代码块,减少了不必要的同步开销

                if (instance == null) {

                    instance = new DCLSingleton();

                }

            }

        }

        return instance;

    }

}

DCL失效:

JDK1.5之前,可能会有DCL实现的问题,上述代码中的20行,在Java里虽然是一句代码,但它并不是一个真正的原子操作。

instance = new DCLSingleton();

它编译成最终的汇编指令,会有下面3个阶段:

1.给DCLSingleton实例分配内存

2.调用DCLSingleton的构造函数,初始化成员变量。

3.将instance指向分配的内存空间(这个操作以后,instance才不为null)

jdk1.5之前,上述的2、3步骤不能保证顺序,也就是说有可能是1-2-3,也有可能是1-3-2。如果是1-3-2,当线程A执行完步骤3(instance已经不为null),但是还没执行完2,线程B又调用了getInstance方法,这时候线程B所得到的就是线程A没有执行步骤2(没有执行完构造函数)的instance,线程B在使用这样的instance时,就有可能会出错。这就是DCL失效。

jdk1.5之后,可以使用volatile关键字,保证汇编指令的执行顺序,虽然会影响性能,但是和程序的正确性比起来,可以忽悠不计。

 

 

import java.util.HashMap;

import java.util.Map;

 

/**

 * 容器单例模式

 */

public class ContainerSingleton {

    private static Map<String, Object> singletonMap = new HashMap<String, Object>();

 

    //单例对象加入到集合,key要保证唯一性

    public static void putSingleton(String key, Object singleton){

        if (key != null && !"".equals(key) && singleton != null) {

            if (!singletonMap.containsKey(key)) {   //这样防止集合里有一个类的两个不同对象

                singletonMap.put(key, singleton);   

            }

        }

    }

 

    //根据key从集合中得到单例对象

    public static Object getSingleton(String key) {

        return singletonMap.get(key);

    }

}

在程序初始化的时候,讲多种单例类型对象加入到一个单例集合里,统一管理。在使用时,通过key从集合中获取单例对象。这种方式多见于系统中的单例,像安卓中的系统级别服务就是采用集合形式的单例模式,比如常用的LayoutInfalter,我们一般在Fragment中的getView方法中使用如下代码:

View view = LayoutInflater.from(context).inflate(R.layout.xxx, null);

其实LayoutInflater.from(context)就是得到LayoutInflater实例,看下面的Android源码:

    /**

     * Obtains the LayoutInflater from the given context.

     */

    public static LayoutInflater from(Context context) {

        //通过key,得到LayoutInflater实例

        LayoutInflater LayoutInflater =

                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        if (LayoutInflater == null) {

            throw new AssertionError("LayoutInflater not found.");

        }

        return LayoutInflater;

    }

原创粉丝点击