There's no hope of "overloading" the new
operator but you could certainly write a custom class loader that simply reloads the bytecode every time you ask it to load a class. No out-of-the-box classloaders will do what you're looking for because they all assume that the class definition will not change through the life of the JVM.
But here's how you make it happen. Create a class loader called, say, Reloader
which overrides the methods loadClass
and findClass
methods so that they simply reload the class files from disk every time they are called (instead of "caching" them for later use). Then you just have to call new Reloader().loadClass("foo.bar.MyClassName")
any time you suspect the class definition has changed (e.g. as part of your testing framework's lifecycle methods).
This article fills in some of the details but misses some important points, especially about using new instances of the classloader for subsequent reloads and delegating to the default classloader when appropriate. Here is a simple working example which repeatedly loads the class MyClass
and assumes its class file exists in the relative "./bin" directory:
public class Reloader extends ClassLoader {
public static void main(String[] args) throws Exception {
do {
Object foo = new Reloader().loadClass("MyFoo").newInstance();
System.out.println("LOADED: " + foo); // Overload MyFoo#toString() for effect
System.out.println("Press <ENTER> when MyFoo.class has changed");
System.in.read();
} while (true);
}
@Override
public Class<?> loadClass(String s) {
return findClass(s);
}
@Override
public Class<?> findClass(String s) {
try {
byte[] bytes = loadClassData(s);
return defineClass(s, bytes, 0, bytes.length);
} catch (IOException ioe) {
try {
return super.loadClass(s);
} catch (ClassNotFoundException ignore) { }
ioe.printStackTrace(System.out);
return null;
}
}
private byte[] loadClassData(String className) throws IOException {
File f = new File("bin/" + className.replaceAll("\.", "/") + ".class");
int size = (int) f.length();
byte buff[] = new byte[size];
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
dis.readFully(buff);
dis.close();
return buff;
}
}
At each invocation of the "do/while" block in the main method, a new Reloader is instantiated which loads the class from disk and returns it to the caller. So if you overwrite the bin/MyClass.class
file to contain a new implementation with a different, overloaded toString
method, then you should see the new implementation each time.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…