前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何避免单例模式被破坏

如何避免单例模式被破坏

作者头像
我是攻城师
发布2018-11-07 16:32:20
1.4K0
发布2018-11-07 16:32:20
举报
文章被收录于专栏:我是攻城师我是攻城师

单例模式几乎每个开发者都会用,但想要写出比较健壮的单例程序,其实并不容易。

这里不再讨论单例的模式的n种写法,仅仅讨论如何避免单例模式被破坏,看下面的一个例子:

代码语言:javascript
复制
public class SimpleSingleton {

    private final static  SimpleSingleton ourInstance = new SimpleSingleton();

    private SimpleSingleton() {
    }

    public static SimpleSingleton getInstance() {
        return ourInstance;
    }
}

这是一个最简单的饿汉式的单例实现,在类进行初始化的时候会安全的创建实例,从而不需要同步。但这么实现,真的能保证任何时候只有一个实例吗?

答案是否定的。

在Java里面,创建对象有4种方式:

(1)new

(2)反射

(3)克隆

(4)反序列化

上面实现的单例,我们通过new确实能保证单例,但是后面的几种方式,都会破坏单例模式。

先说反射的方式,反射在带来的灵活性的同时也破坏了Java封装的特性,通过反射可以访问类里面所有的私有属性和方法。所以反射访问私有构造器是可以非常容易的创建的多个对象实例,从而破坏单例模式。

接着说克隆,这个破坏在大部分时候可以避免,因为想要克隆对象,我们必须实现Cloneable接口,然后重写clone方法,在clone的返回值处,可以返回任何实例。

最后说下序列化和反序列化,如果我们的类没有定义序列化的方法,那么在反序列化的时候,会重新生成一个新的实例,所以这也相当于破坏了单例模式。

最后还有一种不常见的破坏的场景,就是通过我们自定义类加载器来加载类,导致类本身都不是同一个类,这种场景在web项目有多级类加载器的时候比较常见,可以通过一个共用的父加载器来解决这个单例的问题,或者通过需要加载单例的类的时候,使用创建该类本身的加载器去创建,如果不在一个线程里面可以通过线程的上下文来传递类加载器。

我们改下改进后的单例模式:

代码语言:javascript
复制
public class Singleton implements Serializable,Cloneable {

    //在类初始化期间,执行由JVM保证线程安全
    private static Singleton singleton=new Singleton();


    //避免反射和多类加载器破坏
    private Singleton() {
            if (Singleton.singleton != null) {
                throw new InstantiationError("Creating of this object is not allowed.");
            }

    }

    public static Singleton getInstance(){
        return singleton;
    }

    //避免克隆破坏
    @Override
    protected Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Cloning of this class is not allowed");
//        return super.clone();
    }

//    //避免反序列破坏
    protected Object readResolve() {
        return singleton;
    }



}

正确的编写单例模式,其实是需要很多注意事项的,所以在jdk5之后,推荐使用枚举来创建单例类,通过枚举创建的类其实已经帮我们考虑到了上面的所有问题,不用担心其他的一些情况,JVM内部在创建的时候会自动给枚举的类做特殊处理,从而保证其在各种情况下保持唯一的实例。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 我是攻城师 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档