Reputation: 1558
I have a legacy (not modifiable :-() code, that have a static initializer block. I would like to create a subclass - but without running the static block.
Is there any way doing this? For example: class.ForName method has a second argument which is responsibe for initializing the class - but sadly, it doesn't work for me (probably means something else ..): http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName(java.lang.String, boolean, java.lang.ClassLoader)
UPDATE: the above mentioned class.forName does not trigget initialization - but asking for a newInstance does :-(
Thanks, krisy
Upvotes: 3
Views: 1373
Reputation: 4509
You can always patch the old class with ASM. Generating a new class from the old bytecode by ignoring the clinit block should be easy.
import org.objectweb.asm.*;
import org.objectweb.asm.commons.EmptyVisitor;
import java.io.*;
public class ClinitKiller {
public static void main (String[] args) {
final InputStream input = ClinitKiller.class.getResourceAsStream(Test.class.getName() + ".class");
try {
final byte[] bytes = instrument(input);
FileOutputStream out = new FileOutputStream("/tmp/Test.class");
out.write(bytes);
out.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] instrument(InputStream is) throws IOException {
ClassReader reader = new ClassReader(is);
ClassWriter writer = new ClassWriter(0);
ClinitKillerClassAdapter classAdapter = new ClinitKillerClassAdapter(writer);
reader.accept(classAdapter, 0);
return writer.toByteArray();
}
}
class ClinitKillerClassAdapter extends ClassAdapter {
public ClinitKillerClassAdapter(final ClassVisitor cv) {
super(cv);
}
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
if (name.equals("<clinit>")) {
return new EmptyVisitor();
}
return cv.visitMethod(access, name, desc, signature, exceptions);
}
}
Here is the before and after for the following class:
public class Test {
private static final String value;
static {
System.out.println("Test static");
value = "test value";
}
public static void main(String[] args) {
System.out.println(value);
}
}
Before:
javap -c Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3; //Field value:Ljava/lang/String;
6: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: return
static {};
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #5; //String Test static
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: ldc #6; //String test value
10: putstatic #3; //Field value:Ljava/lang/String;
13: return
}
Output:
Test static
test value
After:
javap -c Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #11; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #21; //Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #23; //Field value:Ljava/lang/String;
6: invokevirtual #29; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: return
}
Output:
null
Upvotes: 2
Reputation: 15758
You can do the following:
I know it's not beautiful, but you have hardly another choice. The static initializers will run right after the class is loaded, and there is no possibility to skip it.
Upvotes: 0
Reputation: 1500735
I have a legacy (not modifiable :-() code, that have a static initializer block. I would like to create a subclass - but without running the static block.
You can create a subclass - but you can never initialize that subclass, which means it's probably useless.
From section 12.4.2 of the JLS (class initialization):
Next, if C is a class rather than an interface, and its superclass SC has not yet been initialized, then recursively perform this entire procedure for SC.
Basically I think you'll need to rethink the design. Ideally so that you can cope with the static initializer running.
Upvotes: 2