单例模式Signleton的实现及破坏

来源:互联网 发布:怎么上淘宝网买东西 编辑:程序博客网 时间:2024/06/11 23:38

本篇博客转载自: 点击打开链接 http://www.cnblogs.com/fernandolee24/p/5366720.html

一、单例的实现
Singleton从表面上看是希望并限制该类的实例只能有一个,(比如Runtime类),其构造方式通常是一个private构造函数、static的该类实例、以及返回该实例的getInstance方法。
1、饿汉式(Eager Signleton)
该实例会在类加载的时候自动创建,不管是否使用。
public class DemoSignleton{
    private DemoSignleton(){  }
    private static final DemoSignleton INSTANCE = new DemoSignleton();

    public static DemoSignleton getInstance(){
        return INSTANCE;
    }
}

2、懒汉式(Lazy Signleton)
类实例会在使用的时候才创建。但是由于加了synchronized 所以代价高昂,实际只有在初始,该实例未被创建的时候才有必要加锁,当实例一旦被创建过了就没有必要加锁控制了!
public class LazySignleton {
    private volatile static LazySignleton INSTANCE  = null;
    private LazySignleton(){ }

    pbulic static synchronized LazySignleton getInstance(){
        if(INSTANCE == null)
            INSTANCE = new LazySignleton();
         return INSTANCE;
    }
}
3、懒汉式的改进
当然既然懒汉式有这个缺点,我们可以单独对其处理,如果实例已经创建过了,就不需要进行加锁了,如下是改良后的实例方式:
public static LazySignleton getInstance(){
    if(INSTANCE == null){
        synchronized(LazySignleton.class){
                if(INSTANCE == null){
                    INSTANCE = new LazySignleton();
                }
        }
    }
    return INSTANCE;
}

4、静态内部类实现单例(inner class Signleton)(该方法比较常用)
这种方法有效的解决了并发问题,并延迟了实例化,
public class InnerClassSignleton {
    
    public static class SignletonHolder {
        private static final InnerClassSignleton INSTANCE = new InnerClassSignleton();
    } 

    private InnerClassSignleton(){ }

    public static final InnerClassSignleton getInstance(){
        return SignletonHolder.INSTANCE;
    }
}
二、单例的破坏
有时我们需要对单例进行破坏,下面是几种破坏的方法;
1、第一种比较容易想到的方式就是clone,Java中类通过实现Clonable接口并覆写clone方法就可以完成一个其对象的拷贝。而当Singleton类为Clonable时也自然无法避免可利用这种方式被重新创建一份实例。

2、第二种破坏方式就是利用序列化与反序列化,当Singleton类实现了Serializable接口就代表它是可以被序列化的,该实例会被保存在文件中,需要时从该文件中读取并反序列化成 对象。可就是在反序列化这一过程中不知不觉留下了可趁之机,因为默认的反序列化过程是绕开构造函数直接使用字节生成一个新的对象。

3、前两种破坏方式说到底都是通过避免与私有构造函数正面冲突的方式另辟蹊径来实现的,而这种方式就显得艺高人胆大,既然你是私有的不允许外界直接调用,那么我就利用反射机制强行逼你就范:公开其访问权限。

4、Java中一个类并不是单纯依靠其全包类名来标识的,而是全包类名加上加载它的类加载器共同确定的。因此,只要是用不同的类加载器加载的Singleton类并不认为是相同的。

三、一个真正安全的单例
1、 Safe Signleton
public class SafeSingleton implements Serializable, Cloneable {
 
    private static final long serialVersionUID = -4147288492005226212L;
 
    private static SafeSingleton INSTANCE = new SafeSingleton();
 
    private SafeSingleton() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Singleton instance Already created.");
        }
    }
 
    public static SafeSingleton getInstance() {
        return INSTANCE;
    }
 
    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
 
    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Singleton can't be cloned");
    }
}

在原有Singleton的基础上完善若干方法即可实现一个安全的更为纯正的Singleton。注意到当实例已经存在时试图通过调用私有构造函数会直接报错从而抵御了反射机制的入侵; 让调用clone方法直接报错避免了实例被克隆;覆写readReslove方法直接返回现有的实例本身可以防止反序列化过程中生成新的实例。而对于不同类加载器导致的单例模式破坏暂未找到切实可行的应对方案,请大牛提供高见。


2、Enum Signleton

public enum EnumSingleton{
    INSTANCE;
 
    private EnumSingleton(){
    }
}

采用枚举的方式实现Singleton非常简易,而且可直接通过EnumSingleton.INSTANCE获取该实例。Java中所有定义为enum的类内部都继承了Enum类,而Enum具备的特性包括类加载是静态的来保证线程安全,而且其中的clone方法是final的且直接抛出CloneNotSupportedException异常因而不允许拷贝,同时与生俱来的序列化机制也是直接由JVM掌控的并不会创建出新的实例,此外Enum不能被显式实例化反射破坏也不起作用。当然它也不是没有缺点,比如由于已经隐式继承Enum所以无法再继承其他类了(Java的单继承模式限制),而且相信大多数人并不乐意仅仅为了实现一个纯正的Singleton就将习惯的class修改为enum。


受益很多,再次感谢原创作者: http://www.cnblogs.com/fernandolee24/p/5366720.html

0 0
原创粉丝点击