11

I have a static class (Foo) and a main class (Main)

See 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
    }

}

See Foo.java:

public class Foo {

    public static int i = 0;

}

Is there any way to restart or reset a static class?

Note: I need this because I'm testing a static class with jUnit and I need to clean parameters before second test.


EDIT

ALMOST SOLUTION:

Using StanMax answer, I can to this:

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.");
    }


}

OUTPUT

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.

PROBLEM: if I do the cast bellow:

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

I get error:

java.lang.ClassCastException
Topera
  • 12,223
  • 15
  • 67
  • 104
  • 4
    For this particular example just do `Foo.i = 0;` inside your `Main` method. – LukeH Jan 08 '11 at 00:39
  • You have the ClassCastException because the Foo class loaded by the URLClassLoader is different than the Foo class loaded by the ClassLoader that is running your code (they are not ==). Even if they have the exact same definition, you cannot cast one to the other. The only way this sort of trick works is if Foo implements an interface FooInterface that is loaded by a ClassLoader that is shared between the two classloaders, in which case you can cast to FooInterface. But that doesn't help you access static fields. – NamshubWriter Jan 08 '11 at 22:04
  • NamshubWriter: ok, I Did this....but the main problem is because I'm using a dynamic type....I'm getting "fooClass cannot be resolved to a type". Maybe something in Refletion API can help.... – Topera Jan 09 '11 at 12:43
  • 1
    @Adrian has the right answer. Don't do any of this. If necessary, create a new interface between your test and the static class. The default implementation of the interface will provide the fields of the static class. For testing, you can use EasyMock or just create a new instance of this interface that returns the values you want. Don't mess with the classloader or the garbage collector. – sjr Jan 09 '11 at 19:30
  • Related: It seems that this doesn't solve a problem (or does it) that I'm having with how to properly run a micro-benchmark in Java. This solution forces a class loader to run on every run (which is the entire problem with java benchmarking). I would want all static variables to be reset, including all singletons, etc., so that effectively my entire program can be run twice; the 2nd run would be with everything fully loaded and is the one that is benched. –  Sep 02 '16 at 17:52

4 Answers4

3

Create a static method that sets the class variables to their initial values, then call it when you need it.

martin clayton
  • 76,436
  • 32
  • 213
  • 198
3

Only if you can unload class, get it re-loaded, as class static code gets executed when class is loaded.

But you can just directly modify the value:

Foo.i = 0;

(or create equivalent method for doing it, esp. if static member is not public)

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 1
    No easy way; only works if you actually control ClassLoader that was used to load the class (which you don't unless you actually constructed it). But if you do, you just create a new ClassLoader, load class using it. This will NOT affect existing instances of the "old" class, just creates a new Class definition. – StaxMan Jan 08 '11 at 00:43
2

Avoid static.

It is well known that static is not testable and should thus be avoided. For example, avoiding static is one of the key motivations behind dependency injection. If you need one instance only at runtime, use the singleton pattern instead. And create a new instance for each test run.

akuhn
  • 27,477
  • 2
  • 76
  • 91
  • 3
    Yeah sure sounds great I'll just travel 20 years back in time to tell the people who wrote the Java codebase I'm trying to write tests for "hey don't use static" – ahelwer Nov 25 '20 at 15:42
-7

You can try this.

Main MainObject = new Main;

MainObject.main(args);

It will restart the class again and again until you stop the class.

Lrrr
  • 4,755
  • 5
  • 41
  • 63
Lee
  • 1
  • 1
  • 2