JAVA破坏单例模式的方式以及避免方法

所属分类: 软件编程 / java 阅读数: 56
收藏 0 赞 0 分享

      单例模式,大家恐怕再熟悉不过了,其作用与实现方式有多种,这里就不啰嗦了。但是,咱们在使用这些方式实现单例模式时,程序中就真的会只有一个实例吗?

      聪明的你看到这样的问话,一定猜到了答案是NO。这里笔者就不卖关子了,开门见山吧!实际上,在有些场景下,如果程序处理不当,会无情地破坏掉单例模式,导致程序中出现多个实例对象。

      下面笔者介绍笔者已知的三种破坏单例模式的方式以及避免方法。

1、反射对单例模式的破坏

      我们先通过一个例子,来直观感受一下

    (1)案例

  DCL实现的单例模式:

public class Singleton{
  private static volatile Singleton mInstance;
  private Singleton(){}
  public static Singleton getInstance(){
    if(mInstance == null){
      synchronized (Singleton.class) {
        if(mInstance == null){
          mInstance = new Singleton();
        }
      }
    }
    return mInstance;
  }
}

测试代码:

public class SingletonDemo {

  public static void main(String[] args){
    Singleton singleton = Singleton.getInstance();
    try {
      Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
      constructor.setAccessible(true);
      Singleton reflectSingleton = constructor.newInstance();
      System.out.println(reflectSingleton == singleton);
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

执行结果:

false

      运行结果说明,采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。

    (2)解决方案

      其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第二次实例化的时候,抛出异常。实现代码如下:

public class Singleton{
  private static volatile Singleton mInstance;
  private static volatile boolean mIsInstantiated = false;
  private Singleton(){
    if (mIsInstantiated){
      throw new RuntimeException("Has been instantiated, can not do it again!");
    }
    mIsInstantiated = true;
  }
  public static Singleton getInstance(){
    if(mInstance == null){
      synchronized (Singleton.class) {
        if(mInstance == null){
          mInstance = new Singleton();
        }
      }
    }
    return mInstance;
  }
}

执行结果:

2、clone()对单例模式的破坏

       当需要实现单例的类允许clone()时,如果处理不当,也会导致程序中出现不止一个实例。

    (1)案例

  一个实现了Cloneable接口单例类:

public class Singleton implements Cloneable{
  private static volatile Singleton mInstance;
  private Singleton(){
  }
  public static Singleton getInstance(){
    if(mInstance == null){
      synchronized (Singleton.class) {
        if(mInstance == null){
          mInstance = new Singleton();
        }
      }
    }
    return mInstance;
  }
  @Override
  protected Object clone() throws CloneNotSupportedException {
    // TODO Auto-generated method stub
    return super.clone();
  }
}

测试代码:

public class SingletonDemo {

  public static void main(String[] args){
    try {
      Singleton singleton = Singleton.getInstance();
      Singleton cloneSingleton;
      cloneSingleton = (Singleton) Singleton.getInstance().clone();
      System.out.println(cloneSingleton == singleton);
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
  }
}

执行结果:

false

  (2)解决方案:

     解决思想是,重写clone()方法,调clone()时直接返回已经实例的对象

public class Singleton implements Cloneable{
  private static volatile Singleton mInstance;
  private Singleton(){
  }
  public static Singleton getInstance(){
    if(mInstance == null){
      synchronized (Singleton.class) {
        if(mInstance == null){
          mInstance = new Singleton();
        }
      }
    }
    return mInstance;
  }
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return mInstance;
  }
}

执行结果:

true

3、序列化对单例模式的破坏

   在使用序列化/反序列化时,也会出现产生新实例对象的情况。

  (1)案例

      一个实现了序列化接口的单例类:

public class Singleton implements Serializable{
  private static volatile Singleton mInstance;
  private Singleton(){
  }
  public static Singleton getInstance(){
    if(mInstance == null){
      synchronized (Singleton.class) {
        if(mInstance == null){
          mInstance = new Singleton();
        }
      }
    }
    return mInstance;
  }
}

测试代码:

public class SingletonDemo {

  public static void main(String[] args){
    try {
      Singleton singleton = Singleton.getInstance();
      FileOutputStream fos = new FileOutputStream("singleton.txt");
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(singleton);
      oos.close();
      fos.close();

      FileInputStream fis = new FileInputStream("singleton.txt");
      ObjectInputStream ois = new ObjectInputStream(fis);
      Singleton serializedSingleton = (Singleton) ois.readObject();
      fis.close();
      ois.close();
      System.out.println(serializedSingleton==singleton);
    } catch (Exception e) {
      e.printStackTrace();
    }

  }
}

     运行结果:

false

    (2)解决方案

    在反序列化时的回调方法 readResolve()中返回单例对象。

public class Singleton implements Serializable{
  private static volatile Singleton mInstance;
  private Singleton(){
  }
  public static Singleton getInstance(){
    if(mInstance == null){
      synchronized (Singleton.class) {
        if(mInstance == null){
          mInstance = new Singleton();
        }
      }
    }
    return mInstance;
  }

  protected Object readResolve() throws ObjectStreamException{
    return mInstance;
  }
}

结果:

true

       以上就是笔者目前已知的三种可以破坏单例模式的场景以及对应的解决办法,读者如果知道还有其他的场景,记得一定要分享出来噢,正所谓“独乐乐不如众乐乐”!!!

       单例模式看起来是设计模式中最简单的一个,但“麻雀虽小,五脏俱全”,其中有很多细节都是值得深究的。即便是本篇介绍的这几个场景,也只是介绍了一些梗概而已,很多细节还需要读者自己去试验和推敲的,比如:通过枚举方式实现单例模式,就不存在上述问题,而其它的实现方式似乎都存在上述问题!

       后记

       本篇参(剽)考(窃)了如下资料:https://www.jb51.net/article/143047.htm

以上就是JAVA破坏单例模式的方式以及避免方法的详细内容,更多关于JAVA 单例模式的资料请关注脚本之家其它相关文章!

更多精彩内容其他人还在看

SpringBoot中使用Ehcache的详细教程

EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。这篇文章主要介绍了SpringBoot中使用Ehcache的相关知识,需要的朋友可以参考下
收藏 0 赞 0 分享

在idea 中添加和删除模块Module操作

这篇文章主要介绍了在idea 中添加和删除模块Module操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
收藏 0 赞 0 分享

java spring整合junit操作(有详细的分析过程)

这篇文章主要介绍了java spring整合junit操作(有详细的分析过程),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
收藏 0 赞 0 分享

详解JAVA 弱引用

这篇文章主要介绍了 JAVA 弱引用的相关资料,帮助大家更好的理解和学习java引用对象,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

深入了解JAVA 虚引用

这篇文章主要介绍了JAVA 虚引用的相关资料,帮助大家更好的理解和学习JAVA,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

详解JAVA 强引用

这篇文章主要介绍了JAVA 强引用的相关资料,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

java中的按位与(&)用法说明

这篇文章主要介绍了java中的按位与(&)用法说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
收藏 0 赞 0 分享

深入了解JAVA 软引用

这篇文章主要介绍了JAVA 软引用的相关资料,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
收藏 0 赞 0 分享

利用MyBatis实现条件查询的方法汇总

这篇文章主要给大家介绍了关于利用MyBatis实现条件查询的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用MyBatis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
收藏 0 赞 0 分享

Intellij IDEA 与maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.PathTranslator was bound

这篇文章主要介绍了Intellij IDEA 与maven 版本不符 Unable to import maven project See logs for details: No implementation for org.apache.maven.model.path.Pa
收藏 0 赞 0 分享
查看更多