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

 更新时间:2020年06月16日 11:32:04   作者:宋者为王  
这篇文章主要介绍了JAVA破坏单例模式的方式以及避免方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下

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

      聪明的你看到这样的问话,一定猜到了答案是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 单例模式的资料请关注脚本之家其它相关文章!

相关文章

  • Java实现抽奖功能

    Java实现抽奖功能

    这篇文章主要为大家详细介绍了Java实现抽奖功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • 详解springboot如何更新json串里面的内容

    详解springboot如何更新json串里面的内容

    这篇文章主要为大家介绍了springboot 如何更新json串里面的内容,文中有详细的解决方案供大家参考,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-10-10
  • SpringBoot如何切换成其它的嵌入式Servlet容器(Jetty和Undertow)

    SpringBoot如何切换成其它的嵌入式Servlet容器(Jetty和Undertow)

    这篇文章主要介绍了SpringBoot如何切换成其它的嵌入式Servlet容器(Jetty和Undertow),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Spring boot监控Actuator-Admin实现过程详解

    Spring boot监控Actuator-Admin实现过程详解

    这篇文章主要介绍了Spring boot监控Actuator-Admin实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • java中跨域问题解决的几种方式

    java中跨域问题解决的几种方式

    这篇文章主要给大家介绍了关于java中跨域问题解决的几种方式, 在前后端分离项目中,经常会遇到跨域问题,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • java后台调用接口及处理跨域问题的解决

    java后台调用接口及处理跨域问题的解决

    这篇文章主要介绍了java后台调用接口,处理跨域的问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java 自定义Spring框架与核心功能详解

    Java 自定义Spring框架与核心功能详解

    Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发
    2021-10-10
  • Spring Boot全局异常处理解析

    Spring Boot全局异常处理解析

    这篇文章主要为大家详细介绍了Spring Boot全局异常处理的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • springsecurity 基本使用详解

    springsecurity 基本使用详解

    这篇文章主要介绍了springsecurity 基本使用,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • MyBatis开启二级缓存实现过程解析

    MyBatis开启二级缓存实现过程解析

    这篇文章主要介绍了MyBatis开启二级缓存实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07

最新评论