为了账号安全,请及时绑定邮箱和手机立即绑定

构造函数 newInstance 仅生成本地实例

构造函数 newInstance 仅生成本地实例

慕盖茨4494581 2023-07-13 13:57:39
看来我在测试中错过了一些东西(Robolectrics | Powermockito)。我与 Singleton 有以下课程:public class Core{   private static Core instance = new Core();   public static Core getInstance() {        return instance;    }      public static void destroy(){    instance = null;   } }在我的测试中,我杀死了instancewithCore.destroy() 并因此Core.getInstance()返回null。所以我想再次重新生成每个测试instance。我执行以下操作:Constructor<Core> constructor = Core.class.getDeclaredConstructor();                constructor.setAccessible(true);                Core newCore = constructor.newInstance();所以现在newCore已初始化但Core.getInstance()仍然返回null。如何正确初始化Core-> instance?
查看完整描述

5 回答

?
SMILET

TA贡献1796条经验 获得超4个赞

在谈论单例时,我经常试图向人们解释一个重要的观点:

单例与仅创建 1 个实例的事物之间存在差异。而且,通常,当您认为自己想要一个单例时,实际上您只是想要只创建 1 个实例的东西。

这两件事之间的区别一开始可能并不明显,但认识到这一点很重要,特别是当您发现自己需要在测试之间清除单例的内部状态时。

  • 如果您有一个单例(真正的单例),根据定义,JVM 中可以存在一个实例。如果它具有可变状态,那么这是有问题的,因为这意味着您必须关心该状态。在测试中,您必须清除运行之间的状态,以消除由于测试执行顺序而产生的任何影响;并且您必须连续运行测试。

  • 如果您使用依赖注入(如概念中所示,而不是像 Guice、Dagger、Spring 等任何特定框架),那么使用该实例来自的实例的类并不重要:您作为该类的客户端,得到控制其生命周期。因此,虽然您的生产代码在所有地方使用相同的实例,但您的测试代码可以使用单独的实例 - 因此它们是解耦的- 通常您甚至根本不必担心清理状态,因为您的下一个测试用例可以只需创建该类的一个新实例即可。

因此,不要像Core这样使用类的代码:

class MyClass {

  void foo() {

    Core core = Core.getInstance();

    // ... do stuff with the Core instance.

  }

}

你可以这样写:


class MyClass {

  private final Core core;


  MyClass(Core core) { this.core = core; }


  void foo() {

    // ... do stuff with the Core instance.

  }

}

MyClass并且您已经破坏了和之间的静态绑定Core。您可以MyClass在测试中实例化以下单独的实例Core:


MyClass myClass = new MyClass(new Core());

// Assert something...

或者,如果多个实例需要与同一实例交互Core:


Core core = new Core();

MyClass myClass = new MyClass(core);

MyOtherClass myOtherClass = new MyOtherClass(core);

// Assert something...


查看完整回答
反对 回复 2023-07-13
?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

您应该创建构造函数private,以便使用单例类的代码无法使用它创建实例,并且它们应该只能使用该方法获取实例getInstance()。


此外,单例对象的生命周期通常与 JVM 相关,因为每个 JVM 都应该有一个单例类的实例。因此,如果您可以销毁并重新创建实例,那么它就不是真正的单例(IMO),所以我假设您只想重新创建实例以进行测试。


要在调用该方法后从测试类重新创建单例,destroy()您可以获取Field具有您的类实例的类的 。使用它,Field您可以将其设置为您创建的新实例:


public static void main(String[] args) throws Exception {

        System.out.println(Core.getInstance()); //gets instance

        Core.destroy();

        System.out.println(Core.getInstance()); // null

        reinitializeInstance(Core.class);

        System.out.println(Core.getInstance()); //gets instance

 }


public static void reinitializeInstance(Class<Core> clazz) {

    try {

        Constructor<Core> constructor = clazz.getDeclaredConstructor();

        constructor.setAccessible(true);

        Core newCore = constructor.newInstance();


        Field field = Core.class.getDeclaredField("instance"); //gets the instance field

        field.setAccessible(true);

        field.set(newCore, newCore);


    } catch (Exception e) {

        e.printStackTrace();

    }

}

还有你的 Singleton 类:


class Core {


    private static Core instance = new Core();


    // To prevent reflection from creating a new instance without destroying the first one

    private Core() {

       if(instance != null){

           throw new IllegalStateException("Instance already exists!");

       }

    }


    public static Core getInstance() {

        return instance;

    }


    public static void destroy() {

        instance = null;

    }


}


查看完整回答
反对 回复 2023-07-13
?
慕神8447489

TA贡献1780条经验 获得超1个赞

public class Core {


    private static class SingletonHolder {

        private static AtomicReference<Core> instance = new AtomicReference(new Core());

    }


    public static Core getInstance() {

        return SingletonHolder.instance.get();

    }   


    public static void destroy() {

        SingletonHolder.instance.set(null);

    } 


    public static void reset() {

        SingletonHolder.instance.compareAndSet(null, new Core());

    } 

}

使用额外的“多余”内部类来进行并发初始化,确保静态字段被初始化一次。


更改实例(销毁、重置)需要对对象进行某种同步。synchronize可以使用 AtomicReference来代替成本更高的方法。


compareAndSet如果已有旧值,则不会为实例设置新值。


也是值得拥有的


Optional<Core> getInstance() { ... }

所以使用是有保障的。


Core.getInstance().ifPresent(core -> { ... core ... });


查看完整回答
反对 回复 2023-07-13
?
海绵宝宝撒

TA贡献1809条经验 获得超8个赞

首先,您要使 Core 构造函数可访问,但默认情况下它已经是公共的。

其次,当你调用构造函数时,它只是创建一个新的 Core 实例,它对实例没有任何作用,因为默认创建的构造函数是空的,而且构造函数不是初始化 Singleton 的地方。

如果你想刷新单例实例,你应该有一个专用的方法。


查看完整回答
反对 回复 2023-07-13
?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

换成这个模式怎么样?


public class Core{


   private static Core instance;


   public static Core getInstance() {

        if(instance == null) instance = new Core();

        return instance;

    }   


   public static void destroy(){

    instance = null;

   } 

}

如果您只想在测试中销毁,您可以从 destroy() 方法中删除“public”


查看完整回答
反对 回复 2023-07-13
  • 5 回答
  • 0 关注
  • 148 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信