如何避免单例模式被破坏

Posted 我是攻城师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何避免单例模式被破坏相关的知识,希望对你有一定的参考价值。

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

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

 
   
   
 
  1. public class SimpleSingleton {

  2.    private final static  SimpleSingleton ourInstance = new SimpleSingleton();

  3.    private SimpleSingleton() {

  4.    }

  5.    public static SimpleSingleton getInstance() {

  6.        return ourInstance;

  7.    }

  8. }

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

答案是否定的。

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

(1)new

(2)反射

(3)克隆

(4)反序列化

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

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

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

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

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

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

 
   
   
 
  1. public class Singleton implements Serializable,Cloneable {

  2.    //在类初始化期间,执行由JVM保证线程安全

  3.    private static Singleton singleton=new Singleton();

  4.    //避免反射和多类加载器破坏

  5.    private Singleton() {

  6.            if (Singleton.singleton != null) {

  7.                throw new InstantiationError("Creating of this object is not allowed.");

  8.            }

  9.    }

  10.    public static Singleton getInstance(){

  11.        return singleton;

  12.    }

  13.    //避免克隆破坏

  14.    @Override

  15.    protected Object clone() throws CloneNotSupportedException {

  16.        throw new CloneNotSupportedException("Cloning of this class is not allowed");

  17. //        return super.clone();

  18.    }

  19. //    //避免反序列破坏

  20.    protected Object readResolve() {

  21.        return singleton;

  22.    }

  23. }

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


以上是关于如何避免单例模式被破坏的主要内容,如果未能解决你的问题,请参考以下文章

如何避免反射和序列化破坏单例模式

单例模式--反射--防止序列化破坏单例模式

设计模式 - 单例模式之多线程调试与破坏单例

有意思的java单例模式实现:破坏与防止被破坏

Java Object 序列化与单例模式 [ 转载 ]

如何破坏单例模式