有没有办法将非核心Java类返回到新加载的状态?我希望相当于卸载一个类并从头开始重新加载它。我最关心的是静态初始化器和变量。
问题背景:我正在为学生代码编写一个机器人平地机。我见过的一个常见的学生错误是不恰当地使用静态变量。例如,考虑具有其包含的元素的静态计数的集合。该集合在第一次创建和使用时将正常工作,但在下一次实例化时失败。如果我希望我的测试尽可能模块化,我需要一种方法来在测试后恢复干净状态。
现在我正在或多或少地加载这个类,我已经勾勒出我想要如何使用它。
String classUnderTest = "package.class";
Class unitUnderTest;
try {
unitUnderTest = Class.forName(classUnderTest);
} catch (ClassNotFoundException e) {
System.out.println("Class \"" + classUnderTest + "\" was not found, can't continue.");
printGradeAndExit();
}
// Run foundation tests (stuff on which later tests depend) using unitUnderTest.newInstance()
runFoundationTests(unitUnderTest);
// Now reset unitUnderTest for a static variable detection test
lookForCommonStaticVariableMistakes(unitUnderTest);
显然,机器平地机不能完美,但我想抓住常见的错误,这就是其中之一。
根据Java Language Specification, Section 12.7,支持卸载类是可选的(但是可以完全按照我想要的方式执行)。有没有办法在不依赖非标准功能的情况下做到这一点?
最后一种方法是执行一个Ant构建,它在单独的程序中运行一系列测试,但是如果可能的话,我想在一个过程中完成这项工作。
答案 0 :(得分:3)
您不必为每次运行卸载该类,只需使用新的类加载器即可。您可以创建URLClassLoader
的新实例并使用该实例加载学生代码(而不是将学生代码放在常规应用程序类路径上)。使用该ClassLoader加载目标类,然后使用返回的Class
对象找到您选择的方法并调用它。如果您要调用的方法是非静态的,则必须首先采取一些额外步骤,通过反射创建对象的实例。
使用典型的public static void main(String[])
方法进行输入的示例。
String[] args = new String[0]; //Add arguments for main call.
//Add whatever classpath elements you need.
String[] classpath = {"file:///example/path/to/studentProjectJarFile.jar",
"file:///example/path/to/studentProjectDir/"};
//Create classloader.
ClassLoader cl =
new URLClassLoader(classpath);
Class<?> mainClazz;
Method mainMethod;
//Find Class.
mainClazz = cl.loadClass("com.example.MyClass");
//Find the target method.
mainMethod = mainClazz.getMethod("main", String[].class);
//Invoke method.
mainMethod.invoke(null, (Object) args);
在此示例中,com.example.MyClass
不应位于常规应用程序类路径中。如果它是那个将被使用的版本而不是自定义类加载器找到的版本作为标准类加载器使用父对象委托来加载类。
答案 1 :(得分:1)
坦率地说,我会开始一个新的JVM来评估每个学生的代码。如果您尝试在一个JVM实例中完成所有操作,则一个学生的代码有太多机会干扰另一个学生的代码。像无限循环那样简单......
作为副作用,这在很大程度上消除了对自定义类加载器等的需求。