【问题标题】:Java: how to "restart" a static class?Java:如何“重新启动”静态类?
【发布时间】:2014-11-27 15:27:34
【问题描述】:

我有一个静态类 (Foo) 和一个主类 (Main)

参见 Main.java:

public class Main {

    public static void main(String[] args) {
        System.out.println(Foo.i); // 0
        Foo.i++;
        System.out.println(Foo.i); // 1
        // restart Foo here 
        System.out.println(Foo.i); // 1 again...I need 0
    }

}

参见 Foo.java:

public class Foo {

    public static int i = 0;

}

有没有办法重新启动或重置静态类?

注意:我需要这个,因为我正在使用 jUnit 测试一个静态类,并且我需要在第二次测试之前清理参数。


编辑

几乎可以解决:

使用 StanMax 答案,我可以这样做:

Main.java

public class Main {

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

    public static void test() throws Exception {

        System.out.println("\ntest()");

        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> fooClass = myClassLoader.loadClass(Foo.class.getCanonicalName());

        Object foo = fooClass.newInstance();
        System.out.println("Checking classloader: " + foo.getClass().getClassLoader());

        System.out.println("GC called!");
        System.gc();
    }

}

MyClassLoader.java

public class MyClassLoader {

    private URLClassLoader urlClassLoader;

    public MyClassLoader() {
        try {
            URL url = new File(System.getProperty("user.dir") + "/bin/").toURL();
            URL[] urlArray = {url};  
            urlClassLoader = new URLClassLoader(urlArray, null);  
        } catch (Exception e) {
        }
    }

    public Class<?> loadClass(String name) {
          try {
            return (Class<?>) urlClassLoader.loadClass(name);
        } catch (Exception e) {
        }
        return null;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("MyClassLoader - End.");     
    }


}

Foo.java

public class Foo {

    public static int i = 0;

    static {
        System.out.println("Foo - BEGIN ---------------------------------");
    }

    public void finalize() throws Throwable {
        System.out.println("Foo - End.");
    }


}

输出

test()
Foo - BEGIN ---------------------------------
Checking classloader: java.net.URLClassLoader@ec160c9
GC called!
MyClassLoader - End.
Foo - End.

test()
Foo - BEGIN ---------------------------------
Checking classloader: java.net.URLClassLoader@ec3fb9b
GC called!
MyClassLoader - End.
Foo - End.

问题:如果我做下面的演员:

Foo foo = (Foo) fooClass.newInstance();

我得到错误:

java.lang.ClassCastException

【问题讨论】:

  • 对于这个特定的示例,只需在您的 Main 方法中执行 Foo.i = 0;
  • 您有 ClassCastException,因为 URLClassLoader 加载的 Foo 类与运行代码的 ClassLoader 加载的 Foo 类不同(它们不是 ==)。即使它们具有完全相同的定义,您也不能将一个转换为另一个。这种技巧起作用的唯一方法是,如果 Foo 实现了由两个类加载器共享的 ClassLoader 加载的接口 FooInterface,在这种情况下,您可以强制转换为 FooInterface。但这并不能帮助您访问静态字段。
  • NamshubWriter:好的,我做到了....但主要问题是因为我使用的是动态类型....我得到“fooClass 无法解析为类型”。也许Refletion API中的某些东西可以提供帮助......
  • @Adrian 有正确的答案。不要做任何这些。如有必要,在您的测试和静态类之间创建一个新接口。接口的默认实现将提供静态类的字段。对于测试,您可以使用 EasyMock 或只创建此接口的新实例以返回您想要的值。不要乱用类加载器或垃圾收集器。
  • 相关:这似乎并不能解决我在如何正确运行 Java 中的微基准测试时遇到的问题(或确实解决了问题)。该解决方案强制类加载器在每次运行时都运行(这是 java 基准测试的全部问题)。我希望重置所有静态变量,包括所有单例等,以便我的整个程序可以有效地运行两次;第二次运行将是所有东西都满载,并且是板凳。

标签: static junit java


【解决方案1】:

只有当你可以卸载类时,才能重新加载它,因为类静态代码会在类加载时执行。

但是你可以直接修改值:

Foo.i = 0;

(或创建等效的方法,特别是如果静态成员不是公共的)

【讨论】:

  • 不容易;仅当您实际控制用于加载类的 ClassLoader 时才有效(除非您实际构建它,否则您不会这样做)。但是如果你这样做,你只需创建一个新的 ClassLoader,使用它加载类。这不会影响“旧”类的现有实例,只会创建一个新的类定义。
【解决方案2】:

创建一个静态方法,将类变量设置为其初始值,然后在需要时调用它。

【讨论】:

  • 这是最有效的方法。
  • 我有一个大类,不想重构一切来清理参数......当然,如果我不能重新初始化一个类,我将不得不这样做......谢了!
【解决方案3】:

避免静态。

众所周知,静态是不可测试的,因此应该避免。例如,避免静态是依赖注入背后的关键动机之一。如果您只在运行时需要一个实例,请改用单例模式。并为每次测试运行创建一个新实例。

【讨论】:

  • 是的,听起来不错,我将回到 20 年前,告诉编写 Java 代码库的人我正在尝试为“嘿,不要使用静态”编写测试
【解决方案4】:

你可以试试这个。

Main MainObject = new Main;

MainObject.main(args);

它将一次又一次地重新启动课程,直到您停止课程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-05-09
    • 1970-01-01
    • 2023-04-07
    • 2018-07-07
    • 1970-01-01
    • 2022-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多